// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * @module     block_mwa_dashboard/chat
 * @copyright  2026 Bruno Porto
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
define(['block_mwa_dashboard/dashboardstore', 'core/log', 'core/templates'], function(Store, Log, Templates) {
    'use strict';
    var w = Store.windowFacade();

    (function(){
    'use strict';

    function tr(k,fb){var S=Store.getStrings?Store.getStrings():{};var v=Object.prototype.hasOwnProperty.call(S,k)?S[k]:'';return(v&&!/^\[\[/.test(v))?v:(fb!==undefined?fb:k);}
    function esc(v){return String(v==null?'':v).replace(/[&<>"']/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
    function norm(v){return v==null?'':String(v).trim();}
    function localizeMoodleTerms(value, language){
        var label=norm(value);
        if(String(language||'').indexOf('pt')!==0||!label)return label;
        var translations={
            'Submission graded':'Entrega avaliada',
            'Course module viewed':'Módulo do curso visualizado',
            'Submission submitted':'Entrega enviada',
            'File uploaded':'Arquivo enviado',
            'Quiz attempt submitted':'Tentativa do questionário enviada',
            'Quiz attempt started':'Tentativa do questionário iniciada',
            'Course viewed':'Curso visualizado',
            'Forum post created':'Publicação no fórum criada',
            'Discussion created':'Discussão criada',
            'Discussion viewed':'Discussão visualizada',
            'User logged in':'Usuário entrou no Moodle',
            'User logged out':'Usuário saiu do Moodle',
            'Content created':'Conteúdo criado'
        };
        Object.keys(translations).forEach(function(english){
            label=label.replace(new RegExp(english,'gi'),translations[english]);
        });
        return label;
    }
    function $(id){return document.getElementById(id);}
    function renderTemplate(node, template, context){
        if(!node)return Promise.resolve(null);
        return Templates.render('block_mwa_dashboard/'+template, context||{}).then(function(html, js){
            Templates.replaceNodeContents(node, html, js);
            return node;
        }).catch(function(e){Log.error(e);return node;});
    }
    /** Inline markdown: bold, italic and inline code. Input must already be escaped. */
    function inlineMd(s){
        return s
            .replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>')
            .replace(/(^|[^*])\*([^*]+)\*(?!\*)/g,'$1<em>$2</em>')
            .replace(/`([^`]+)`/g,'<code>$1</code>');
    }

    /**
     * Renders the assistant's markdown as real elements. The model tends to
     * answer with headings, rules and bullet lists, and previously only bold
     * was handled, so the raw "##", "---" and "-" markers leaked into the
     * bubble. Escaping happens first, so nothing here can inject HTML.
     */
    function richText(value){
        var lines = esc(value).split('\n');
        var out = [];
        var listTag = '';
        function closeList(){
            if(listTag){ out.push('</' + listTag + '>'); listTag = ''; }
        }
        function openList(tag){
            if(listTag !== tag){ closeList(); out.push('<' + tag + ' class="chat-md-list">'); listTag = tag; }
        }
        lines.forEach(function(raw){
            var t = raw.trim();
            if(!t){ closeList(); return; }
            if(/^(-{3,}|\*{3,}|_{3,})$/.test(t)){ closeList(); out.push('<hr class="chat-md-hr">'); return; }
            var h = t.match(/^(#{1,6})\s*(.*)$/);
            if(h){
                closeList();
                out.push('<div class="chat-md-h chat-md-h' + h[1].length + '">' + inlineMd(h[2]) + '</div>');
                return;
            }
            var ol = t.match(/^\d+[.)]\s+(.*)$/);
            if(ol){ openList('ol'); out.push('<li>' + inlineMd(ol[1]) + '</li>'); return; }
            var ul = t.match(/^[-*+]\s+(.*)$/);
            if(ul){ openList('ul'); out.push('<li>' + inlineMd(ul[1]) + '</li>'); return; }
            closeList();
            out.push('<p class="chat-md-p">' + inlineMd(t) + '</p>');
        });
        closeList();
        return out.join('');
    }
    function parseDate(l){
        if(l._ts)return new Date(Number(l._ts)*1000);
        var s=norm(l.hora);var m=s.match(/(\d{2})\/(\d{2})\/(\d{2}),?\s*(\d{2}):(\d{2})/);
        if(m)return new Date(2000+Number(m[3]),Number(m[2])-1,Number(m[1]),Number(m[4]),Number(m[5]));
        return null;
    }

    var CONVS     = [];
    var CUR_ID    = null;
    var BUSY      = false;
    var _initDone = false;

    function save(){
        try{sessionStorage.setItem('mwa_chat_convs',JSON.stringify(CONVS.slice(-20)));}catch(e){}
    }
    function load(){
        try{
            var s=sessionStorage.getItem('mwa_chat_convs');
            if(s)CONVS=JSON.parse(s)||[];
        }catch(e){CONVS=[];}
    }

    function getCur(){return CONVS.find(function(c){return c.id===CUR_ID;});}

    function newConv(){
        var id='conv_'+Date.now();
        CONVS.unshift({id:id,title:tr('chat_new_conv','Nova conversa'),createdAt:Date.now(),messages:[]});
        CUR_ID=id;
        renderSidebar();
        showWelcome();
        var ti=$('chatConvTitle');if(ti)ti.textContent=tr('chat_assistant_name','Assistente da turma');
    }

    function deleteConv(id){
        CONVS=CONVS.filter(function(c){return c.id!==id;});
        if(CUR_ID===id)CUR_ID=CONVS.length?CONVS[0].id:null;
        if(!CUR_ID)newConv();
        else{renderSidebar();renderMessages();}
        save();
    }

    function buildContext(){
        var mwa=w.MWADashboard||{};
        var state=(mwa&&mwa.state)||{};
        var logs=state.logs||[];
        var grades=state.grades||[];
        var cfg=Store.getConfig?Store.getConfig():{};
        var isPortuguese=String(cfg.language||'pt_br').indexOf('pt')===0;
        if(!logs.length)return null;
        var data=logs.map(function(r){var d=parseDate(r);return d?Object.assign({},r,{_date:d}):null;}).filter(Boolean);
        /* The roster must come from the dashboard state, which merges the logs
           with the grade export. Building it from logs alone silently dropped
           every student who never accessed Moodle - precisely the ones the
           teacher most needs the assistant to talk about. */
        var dashboardStudents=state.students||[];
        var students=[];var stuSet={};
        function addStudent(n){
            n=norm(n);
            if(n&&!stuSet[n]){stuSet[n]=1;students.push(n);}
        }
        dashboardStudents.forEach(function(s){addStudent(s.name);});
        data.forEach(function(r){addStudent(r.nomecompleto);});
        var la={};
        data.forEach(function(r){
            var n=norm(r.nomecompleto);
            if(!la[n]||r._date>la[n])la[n]=r._date;
        });
        var now=new Date();
        /* Math.floor and a null for "never accessed", matching EngagementCalc.
           The old version rounded and returned 99, so the chat disagreed with
           the dashboard by a day and invented a fake 99-day figure. */
        function daysAgo(d){if(!d)return null;return Math.max(0,Math.floor((now-d)/86400000));}
        // grades rows: {__mwa_type__:'student','First name':...,'Last name':...,'Course total (Grade)':N}
        var gradeMap={};
        var gradeDetailMap={}; // student name -> {actName: value}
        var actNamesRow=grades&&grades[0]&&grades[0]['__mwa_type__']==='activity_names'?grades[0]:null;
        grades.forEach(function(g){
            if(!g||g['__mwa_type__']!=='student')return;
            var fn=norm((g['First name']||'')+(g['Last name']?' '+g['Last name']:''));
            if(!fn)return;
            var total=g['Course total (Grade)'];
            if(total!=null&&total!=='-')gradeMap[fn]=Number(total);
            // Per-activity grades
            if(actNamesRow){
                gradeDetailMap[fn]={};
                Object.keys(actNamesRow).forEach(function(k){
                    if(k==='__mwa_type__'||/^act_(cmid|module|available|availablefrom|availableuntil|duedate)_/.test(k))return;
                    var actName=actNamesRow[k];
                    var val=g[k];
                    if(actName&&val!=null&&val!=='-')gradeDetailMap[fn][actName]=val;
                });
            }
        });
        var gVals=Object.values(gradeMap);
        var avgGrade=gVals.length?Math.round(gVals.reduce(function(s,v){return s+v;},0)/gVals.length):null;
        /* Engagement scores come straight from the dashboard state, which is
           where the shared EngagementCalc result is already stored. */
        var evData=state.students||[];
        var atRisk=0;
        /* Caps keep the prompt within budget. Every list built from them is
           reported to the model with its real total, so it never presents a
           truncated list as the whole picture. */
        var STUDENT_CAP=200;
        var ACTIVITY_CAP=60;
        var summaries=students.slice(0,STUDENT_CAP).map(function(n){
            var ago=daysAgo(la[n]);
            var ev=evData.find(function(x){return x.name===n;})||null;
            var score=ev?ev.score:null;
            var interactions=data.filter(function(r){return norm(r.nomecompleto)===n;}).length;
            var grade=gradeMap[n]!=null?gradeMap[n]:null;
            /* Same threshold as the Action Center KPI, so the assistant and the
               dashboard never report a different number of at-risk students. */
            if(score!=null&&score<=40)atRisk++;
            var actGrades=gradeDetailMap[n]||{};
            var actGradeStr=Object.keys(actGrades).map(function(k){return k+': '+actGrades[k];}).join(', ');
            return {
                nome:n,
                diasSemAcesso:ago==null?'never accessed':ago,
                interacoes:interactions,
                engScore:score!=null?score+'%':'—',
                notaTotal:grade!=null?grade.toFixed(1):'—',
                notasPorAtividade:actGradeStr||'—',
            };
        });

        var dashboardActivities=state.activities||[];
        function low(v){return norm(v).toLowerCase();}
        function localizedEventLabel(value){
            var label=norm(value);
            if(!isPortuguese||!label)return label;
            var translations={
                'Submission graded':'Entrega avaliada',
                'Course module viewed':'Módulo do curso visualizado',
                'Submission submitted':'Entrega enviada',
                'File uploaded':'Arquivo enviado',
                'Quiz attempt submitted':'Tentativa do questionário enviada',
                'Quiz attempt started':'Tentativa do questionário iniciada',
                'Course viewed':'Curso visualizado',
                'Forum post created':'Publicação no fórum criada',
                'Discussion created':'Discussão criada',
                'Discussion viewed':'Discussão visualizada',
                'User logged in':'Usuário entrou no Moodle',
                'User logged out':'Usuário saiu do Moodle',
                'Content created':'Conteúdo criado'
            };
            Object.keys(translations).forEach(function(english){
                label=label.replace(new RegExp(english,'gi'),translations[english]);
            });
            return label;
        }
        function sameName(a,b){return low(a)===low(b);}
        function matchActivity(a,b){
            var aa=low(a),bb=low(b);
            return aa&&bb&&(aa===bb||aa.indexOf(bb)>=0||bb.indexOf(aa)>=0);
        }
        function studentModel(name){
            var ln=low(name);
            return dashboardStudents.find(function(s){return sameName(s.name,name)||low(s.email)===ln;})||null;
        }
        function logActivityName(r){
            return localizedEventLabel(r.contextodoevento||r.context||r.eventcontext||r._resource||r.nomedoevento);
        }
        function logIsSubmission(r){
            var text=low([r.nomedoevento,r.action,r.componente,r.component,r._modtype].join(' '));
            return text.indexOf('submit')>=0||text.indexOf('submitted')>=0||text.indexOf('submission')>=0||
                text.indexOf('upload')>=0||text.indexOf('graded')>=0||text.indexOf('submetid')>=0||
                text.indexOf('tentativa enviada')>=0||text.indexOf('attempt submitted')>=0||
                text.indexOf('h5p completion')>=0||text.indexOf('completed')>=0;
        }
        var submittedByStudent={};
        var accessedByStudent={};
        data.forEach(function(r){
            var sn=norm(r.nomecompleto);
            var an=logActivityName(r);
            if(!sn||!an)return;
            accessedByStudent[sn]=accessedByStudent[sn]||{};
            accessedByStudent[sn][low(an)]=an;
            if(logIsSubmission(r)){
                submittedByStudent[sn]=submittedByStudent[sn]||{};
                submittedByStudent[sn][low(an)]=an;
            }
        });

        // Activities: names and access counts from logs
        // Build activity map using full name from the event context field
        // Key = lowercase for dedup, but preserve original name
        var actMap={};
        data.forEach(function(r){
            // Prefer event context field (real activity name) over event name field (action label)
            var rawName=(r.contextodoevento&&r.contextodoevento.length>2)?r.contextodoevento:(r.nomedoevento||'');
            rawName=localizedEventLabel(rawName);
            rawName=rawName.trim();
            if(!rawName||rawName.length<2)return;
            // Skip generic module-type names (assign, forum, quiz, page, etc. without spaces)
            if(rawName.indexOf(' ')<0&&rawName.length<10)return;
            var key=rawName.toLowerCase();
            if(!actMap[key])actMap[key]={name:rawName,type:(r._modtype||'').toLowerCase()||'outro',accessed:0,students:{}};
            // Update name if longer (more descriptive)
            if(rawName.length>actMap[key].name.length)actMap[key].name=rawName;
            actMap[key].accessed++;
            if(r.nomecompleto)actMap[key].students[norm(r.nomecompleto)]=1;
        });
        // Also enrich from MWA_ACT_NAMES (grade items)
        var actNamesMap=w.MWA_ACT_NAMES||{};
        var gradedMeta=[];
        if(actNamesRow){
            Object.keys(actNamesRow).forEach(function(k){
                var m=k.match(/^act_(\d+)$/);
                if(!m)return;
                var seq=m[1];
                var name=norm(actNamesRow[k]);
                if(!name)return;
                gradedMeta.push({
                    nome:name,
                    modulo:norm(actNamesRow['act_module_'+seq]||''),
                    cmid:parseInt(actNamesRow['act_cmid_'+seq]||0,10)||0,
                    disponivel:String(actNamesRow['act_available_'+seq]||'1')!=='0',
                    abreEm:parseInt(actNamesRow['act_availablefrom_'+seq]||0,10)||0,
                    entregaEm:parseInt(actNamesRow['act_duedate_'+seq]||0,10)||0,
                    fechaEm:parseInt(actNamesRow['act_availableuntil_'+seq]||0,10)||0
                });
            });
        }
        var gradedActivities=gradedMeta.length?gradedMeta.map(function(a){return a.nome;}):Object.values(actNamesMap).filter(function(n){return n&&String(n).length>2;});

        var allStudentNames=students.slice(0,STUDENT_CAP);
        // Activity coverage follows the activity cards: only students with at least one Moodle interaction
        // are part of the denominator. Students with zero interactions are not counted in coverage.
        var interactingStudentNames=allStudentNames.filter(function(n){
            return data.some(function(r){return norm(r.nomecompleto)===n;});
        });
        var activities=Object.values(actMap)
            .sort(function(a,b){return b.accessed-a.accessed;})
            .slice(0,ACTIVITY_CAP)
            .map(function(a){
                var isGraded=gradedActivities.some(function(g){
                    var gn=norm(String(g)).toLowerCase();var an=norm(a.name).toLowerCase();
                    return gn===an||(gn.length>4&&(gn.indexOf(an)>=0||an.indexOf(gn)>=0));
                });
                var studentsWhoAccessed=Object.keys(a.students);
                return {
                    nome:a.name,
                    tipo:a.type,
                    acessos:a.accessed,
                    alunosUnicos:studentsWhoAccessed.length,
                    cobertura:Math.round((studentsWhoAccessed.length/Math.max(interactingStudentNames.length,1))*100),
                    faltam:Math.max(interactingStudentNames.length-studentsWhoAccessed.length,0),
                    temNota:isGraded,
                };
            });

        // Use the exact activity/resource dataset used by the Activities tab.
        // This prevents the chat from independently reconstructing access coverage
        // and guarantees that CMID, unique students, missing students and coverage
        // match the activity cards.
        if (window.MWAActivities && typeof window.MWAActivities.getChatSnapshot === 'function') {
            var cardActivities = window.MWAActivities.getChatSnapshot();
            if (Array.isArray(cardActivities) && cardActivities.length) {
                activities = cardActivities.map(function(a) {
                    var isGraded = gradedMeta.some(function(g) {
                        if (a.cmid && g.cmid) return Number(a.cmid) === Number(g.cmid);
                        return matchActivity(g.nome, a.nome);
                    });
                    return {
                        nome: a.nome,
                        tipo: a.tipo,
                        acessos: a.acessos,
                        alunosUnicos: a.alunosUnicos,
                        cobertura: a.cobertura,
                        faltam: a.faltam,
                        temNota: isGraded
                    };
                }).slice(0, ACTIVITY_CAP);
            }
        }

        // Alerts summary
        summaries.forEach(function(s){
            var ds=studentModel(s.nome);
            var actGrades=gradeDetailMap[s.nome]||{};
            var submitted=submittedByStudent[s.nome]||{};
            var accessed=accessedByStudent[s.nome]||{};
            var pendingActivities=[];
            var pendingGrades=[];
            var deliveredActivities=[];
            gradedActivities.forEach(function(a){
                var hasGrade=Object.keys(actGrades).some(function(k){return matchActivity(k,a);});
                var hasSubmission=Object.keys(submitted).some(function(k){return matchActivity(k,a);});
                if(hasSubmission||hasGrade)deliveredActivities.push(a);
                if(!hasSubmission&&!hasGrade)pendingActivities.push(a);
                if(hasSubmission&&!hasGrade)pendingGrades.push(a);
            });
            var resourcesNotAccessed=activities.filter(function(a){
                return !a.temNota&&!Object.keys(accessed).some(function(k){return matchActivity(k,a.nome);});
            }).map(function(a){return a.nome;});
            if(ds){
                /* Dashboard values win over the locally recomputed ones so the
                   assistant quotes exactly what the teacher sees on screen. */
                if(ds.interactions!=null)s.interacoes=ds.interactions;
                if(ds.daysWithoutAccess!=null)s.diasSemAcesso=ds.daysWithoutAccess;
                else if(ds.last==null)s.diasSemAcesso='never accessed';
                if(ds.grade!=null)s.notaTotal=Number(ds.grade).toFixed(1);
                s.score=ds.score!=null?Math.round(ds.score):null;
                s.cobertura=ds.coverage!=null?Math.round(ds.coverage):null;
                s.conclusao=ds.completion!=null?Math.round(ds.completion):null;
                s.diasAtivos=ds.activeDays!=null?ds.activeDays:null;
                s.itensComNota=ds.gradeItems||gradedActivities.length;
                s.notasLancadas=ds.gradeLaunched||Object.keys(actGrades).length;
            }else{
                s.score=s.engScore&&s.engScore!=='-'?Number(String(s.engScore).replace('%','')):null;
                s.cobertura=null;
                s.conclusao=null;
                s.diasAtivos=null;
                s.itensComNota=gradedActivities.length;
                s.notasLancadas=Object.keys(actGrades).length;
            }
            s.atividadesEntregues=deliveredActivities;
            s.atividadesPendentes=pendingActivities;
            s.notasPendentes=pendingGrades;
            s.conteudosNaoAcessados=resourcesNotAccessed;
        });

        // Peak access hour
        var hourCount={};
        data.forEach(function(r){if(r._date)hourCount[r._date.getHours()]=(hourCount[r._date.getHours()]||0)+1;});
        var peakHour=Object.keys(hourCount).sort(function(a,b){return hourCount[b]-hourCount[a];})[0];
        var modelStudents=dashboardStudents.length?dashboardStudents:summaries;
        atRisk=modelStudents.filter(function(s){return s.score!=null&&Number(s.score)<=40;}).length;
        var withoutRecentAccess=modelStudents.filter(function(s){
            if(s.daysWithoutAccess!=null)return Number(s.daysWithoutAccess)>=7;
            if(s.diasSemAcesso==='never accessed')return true;
            return s.last==null;
        }).length;
        var withInteractions=modelStudents.filter(function(s){return Number(s.interactions||s.interacoes||0)>0;}).length;
        var approved=modelStudents.filter(function(s){
            var g=s.grade!=null?s.grade:(gradeMap[s.nome]!=null?gradeMap[s.nome]:null);
            return g!=null&&Number(g)>=60;
        }).length;
        function avgField(list,field){
            var vals=list.map(function(s){return Number(s[field]);}).filter(function(v){return !isNaN(v);});
            return vals.length?Math.round(vals.reduce(function(a,b){return a+b;},0)/vals.length):null;
        }
        var lowCoverageActivities=activities.filter(function(a){return a.cobertura<60;});
        var gradedPendingTotal=summaries.reduce(function(total,s){return total+(s.atividadesPendentes?s.atividadesPendentes.length:0);},0);

        return {
            curso:{
                nomeCurso:cfg.coursename||tr('chat_unknown_course'),
                totalAlunos:students.length,
                alunosComInteracoes:withInteractions,
                totalInteracoes:data.length,
                mediaInteracoesPorAluno:Math.round(data.length/Math.max(withInteractions,1)),
                alunosEmRisco:atRisk,
                alunosSemAcessoRecente:withoutRecentAccess,
                notaMedia:avgGrade,
                aprovados:approved,
                mediaCobertura:avgField(modelStudents,'coverage'),
                mediaConclusao:avgField(modelStudents,'completion'),
                atividadesAvaliativas:gradedActivities.length,
                pendenciasAvaliativas:gradedPendingTotal,
                recursosBaixaCobertura:lowCoverageActivities.length,
                horarioPico:peakHour?peakHour+'h':'—',
                atividadesComNota:gradedActivities.slice(0,ACTIVITY_CAP),
                atividadesAvaliativasDetalhes:gradedMeta.slice(0,ACTIVITY_CAP),
                baixaCobertura:lowCoverageActivities.slice(0,ACTIVITY_CAP),
            },
            atividades:activities,
            estudantes:summaries.map(function(s){
                return {
                    nomeLocal:s.nome,
                    nota:s.notaTotal,
                    interacoes:s.interacoes,
                    diasSemAcesso:s.diasSemAcesso,
                    engajamento:s.score,
                    cobertura:s.cobertura,
                    conclusao:s.conclusao,
                    diasAtivos:s.diasAtivos,
                    atividadesPendentes:s.atividadesPendentes||[],
                    notasPendentes:s.notasPendentes||[],
                    atividadesEntregues:s.atividadesEntregues||[],
                    conteudosNaoAcessados:s.conteudosNaoAcessados||[]
                };
            }),
            totais:{
                alunos:students.length,
                atividades:Object.keys(actMap).length,
                atividadesListadas:activities.length,
                recursosBaixaCobertura:lowCoverageActivities.length,
            },
        };
    }

    function showWelcome(){
        var messagesEl=$('chatMessages');
        var sugEl=$('chatSuggestions');
        if(!messagesEl)return;

        var hasData=((w.MWADashboard||{}).state||{}).logs&&((w.MWADashboard||{}).state||{}).logs.length>0;

        renderTemplate(messagesEl, 'chat_welcome', {
            message: hasData
                ? tr('chat_welcome_data','Ola! Recebo somente indicadores agregados da turma e nao identifico estudantes para a IA externa.')
                : tr('chat_welcome_nodata',tr('chat_welcome_nodata','Ola! Carregue os dados da turma para que eu possa analisar e responder perguntas sobre os alunos.'))
        });

        if(sugEl&&hasData){
            var sugs=[
                tr('chat_sug1','Quem esta em risco de evadir esta semana?'),
                tr('chat_sug2','Quais alunos devo priorizar hoje?'),
                tr('chat_sug3','Como esta o engajamento geral da turma?'),
                tr('chat_sug4','Escreva um email para alunos com nota abaixo de 60'),
                tr('chat_sug5','Quem melhorou mais nos ultimos dias?'),
                tr('chat_sug6','Faca um resumo executivo da turma'),
            ];
            renderTemplate(sugEl, 'chat_suggestions', {
                suggestions: sugs.map(function(s){return {text:s};})
            });
        }else if(sugEl){sugEl.replaceChildren();}
    }
    function renderSidebar(){
        var el=$('chatHistoryList');if(!el)return;
        renderTemplate(el, 'chat_sidebar', {
            emptylabel: tr('chat_no_convs'),
            deletelabel: tr('chat_delete_conv'),
            conversations: CONVS.slice(0,15).map(function(c){
                return {
                    id: c.id,
                    title: c.title.slice(0,26),
                    date: new Date(c.createdAt).toLocaleDateString(undefined,{day:'2-digit',month:'2-digit'}),
                    active: c.id===CUR_ID,
                    deletelabel: tr('chat_delete_conv')
                };
            })
        });
    }
    function renderMessages(){
        var el=$('chatMessages');
        var sugEl=$('chatSuggestions');
        if(!el)return;

        var conv=getCur();
        if(!conv||!conv.messages.length){showWelcome();return;}
        if(sugEl)sugEl.replaceChildren();

        var ti=$('chatConvTitle');if(ti)ti.textContent=conv.title;

        renderTemplate(el, 'chat_messages', {messages: conv.messages.map(function(m){
            var isAI=m.role==='assistant';
            return {
                align: isAI?'flex-start':'flex-end',
                direction: isAI?'':'flex-direction:row-reverse;',
                avatarbg: isAI?'var(--blue-dim)':'var(--panel3)',
                avatarlabel: isAI?'*':'Eu',
                bubblestyle: isAI
                    ?'background:var(--panel2);border:1px solid var(--line);border-radius:2px 12px 12px 12px;'
                    :'background:var(--blue-dim);border:1px solid rgba(79,142,247,.2);border-radius:12px 2px 12px 12px;',
                contenthtml: richText(m.content)
            };
        })}).then(function(){el.scrollTop=el.scrollHeight;});
    }
    function sendMsg(text){
        var input=$('chatInput')||$('chatInputEl');
        if(input){input.value=text;input.style.height='auto';}
        send();
    }

    function send(){
        if(BUSY)return;
        var input=$('chatInput')||$('chatInputEl');
        var text=((input&&input.value)||'').trim();
        if(!text)return;

        var cfg_s=Store.getConfig?Store.getConfig():{};
        if(!cfg_s.ia_enabled){
            alert(tr('ai_unavailable_message','🔒 Os recursos de Inteligência Artificial estão indisponíveis. Configure uma chave de API válida na administração do MWA.'));
            return;
        }
        var state=((w.MWADashboard||{}).state)||{};
        if(!(state.logs&&state.logs.length)){
            alert(tr('chat_load_data_first','Carregue os dados da turma primeiro.'));
            return;
        }

        if(!CUR_ID)newConv();
        var conv=getCur();if(!conv)return;
        conv.messages.push({role:'user',content:text});
        if(conv.messages.length===1)conv.title=text.slice(0,35)+(text.length>35?'…':'');
        if(input){input.value='';input.style.height='auto';}
        var sugEl=$('chatSuggestions');if(sugEl)sugEl.replaceChildren();
        renderMessages();
        var messagesEl=$('chatMessages');
        var typing=document.createElement('div');
        typing.id='chatTyping';
        typing.style.cssText='display:flex;gap:.5rem;align-items:center;padding:.3rem 0;';
        renderTemplate(typing, 'chat_typing', {}).then(function(){
            if(messagesEl){messagesEl.appendChild(typing);messagesEl.scrollTop=messagesEl.scrollHeight;}
        });

        BUSY=true;
        var btn=$('chatSendBtn');if(btn)btn.disabled=true;
        var ctx=buildContext();
        var cfg=Store.getConfig?Store.getConfig():{};
        var courseid=parseInt(cfg.courseid||0,10);
        var courseName=(ctx&&ctx.curso&&ctx.curso.nomeCurso)||tr('chat_unknown_course','não identificado');
        var lang=cfg.language||'pt_br';
        var langInstr=lang.indexOf('pt')===0
            ?tr('chat_lang_instr_pt','Responda sempre em português brasileiro, de forma direta e prática.')
            :lang.indexOf('es')===0
            ?tr('chat_lang_instr_es','Responda siempre en español, de forma directa y práctica.')
            :'Always respond in English, in a direct and practical way.';
        var langLabel=lang.indexOf('pt')===0?'portuguese-br':lang.indexOf('es')===0?'spanish':'english';

        var histMsgs=[];
        conv.messages.forEach(function(m){histMsgs.push(m);});
        function _chatDone(){
            BUSY=false;
            var t=$('chatTyping');if(t)t.remove();
            if(btn)btn.disabled=false;
            save();
            renderSidebar();
            renderMessages();
        }
        Store.callAction('block_mwa_dashboard_chat_message',{
            courseid:  courseid,
            messages:  JSON.stringify(histMsgs),
            context:   JSON.stringify(ctx||{}),
        }).then(function(res){
            var reply=(res&&res.reply)||'';
            reply=localizeMoodleTerms(reply,lang);
            conv.messages.push({role:'assistant',content:reply||tr('chat_no_reply','Não consegui gerar uma resposta. Tente novamente.')});
            _chatDone();
        }).catch(function(e){
            Log.error(e);
            conv.messages.push({role:'assistant',content:tr('chat_error','Erro ao conectar com a IA')+': '+esc(e.message||String(e))});
            _chatDone();
        });
    }

    function updateContextChips(){
        var el=$('chatContextChips');if(!el)return;
        var cfg=Store.getConfig?Store.getConfig():{};
        var state=((w.MWADashboard||{}).state)||{};
        var logs=state.logs||[];
        var grades=state.grades||[];
        var chips=[];
        if(cfg.ia_enabled){
            var provider=cfg.ia_provider||'IA';
            chips.push({background:'rgba(167,139,250,.15)',color:'#a78bfa',border:'rgba(167,139,250,.3)',label:'* '+provider});
        } else {
            chips.push({background:'var(--red-dim)',color:'var(--red)',border:'rgba(240,101,112,.3)',label:tr('chat_ia_not_configured','AI not configured')});
        }
        if(logs.length){
            var studs=new Set();
            logs.forEach(function(r){if(r.nomecompleto)studs.add(norm(r.nomecompleto));});
            chips.push({background:'var(--green-dim)',color:'var(--green)',border:'var(--green-dim)',label:studs.size+' alunos'});
        }
        renderTemplate(el, 'chat_chips', {chips:chips});
        var sub=$('chatConvSub');
        if(sub)sub.textContent=logs.length
            ?tr('chat_data_ready','Dados carregados — pronto para analisar')
            :tr('chat_no_data_sub','Carregue dados para ativar o chat');
    }

    function render(){
        if(!_initDone){
            _initDone=true;
            load();
            if(!CUR_ID&&CONVS.length)CUR_ID=CONVS[0].id;
            setupDelegation();
        }
        updateContextChips();
        renderSidebar();
        var inp=$('chatInput')||$('chatInputEl');
        if(inp)inp.placeholder=tr('chat_input_placeholder','Pergunte sobre a turma, peça análises ou relatórios...');
        if(getCur())renderMessages();
        else showWelcome();
    }

    var _delegationInstalled=false;
    function setupDelegation(){
        if(_delegationInstalled)return;
        _delegationInstalled=true;
        var page=document.getElementById('page-chat');
        if(!page)return;
        page.addEventListener('click',function(ev){
            var t=ev.target;
            if(t.closest('[data-chat-new]')){newConv();return;}
            var load=t.closest('[data-chat-load]');
            if(load){
                CUR_ID=load.getAttribute('data-chat-load');
                renderSidebar();
                renderMessages();
                return;
            }
            var del=t.closest('[data-chat-del]');
            if(del){deleteConv(del.getAttribute('data-chat-del'));return;}
            var sug=t.closest('[data-chat-sug]');
            if(sug){sendMsg(sug.getAttribute('data-chat-sug'));return;}
            if(t.closest('#chatSendBtn')){send();return;}
        });
        var input=$('chatInput');
        if(input){
            input.addEventListener('keydown',function(e){
                if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send();}
            });
            input.addEventListener('input',function(){
                this.style.height='auto';
                this.style.height=Math.min(this.scrollHeight,120)+'px';
            });
        }
        var newBtn=$('chatNewBtn');
        if(newBtn)newBtn.addEventListener('click',newConv);
    }

    w.MWAChat={render:render,send:send};

    })();
    return w.MWAChat;
});
