"""
Local LLM Component - Transformers.js + ONNX Integration.

Ermoeglicht Header-Extraktion aus Klausuren direkt im Browser:
- Laeuft vollstaendig lokal (Privacy-by-Design)
- ONNX Modell wird beim PWA-Install gecacht
- Extrahiert: Namen, Klasse, Fach, Datum

Architektur:
1. Service Worker cacht ONNX Modell (~100MB)
2. Transformers.js laedt Modell aus Cache
3. Header-Region wird per Canvas extrahiert
4. Vision-Modell extrahiert strukturierte Daten
"""


class LocalLLMComponent:
    """PWA Local LLM Component fuer Header-Extraktion."""

    # Modell-Konfiguration
    MODEL_ID = "breakpilot/exam-header-extractor"
    MODEL_CACHE_NAME = "breakpilot-llm-v1"
    MODEL_SIZE_MB = 100

    @staticmethod
    def get_css() -> str:
        """CSS fuer Local LLM UI-Elemente."""
        return """
        /* Local LLM Status Indicator */
        .local-llm-status {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 8px 12px;
            background: var(--bp-surface-elevated);
            border-radius: 8px;
            font-size: 13px;
        }

        .local-llm-status.loading {
            background: var(--bp-warning-bg);
        }

        .local-llm-status.ready {
            background: var(--bp-success-bg);
        }

        .local-llm-status.error {
            background: var(--bp-error-bg);
        }

        .llm-status-dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: var(--bp-text-muted);
        }

        .local-llm-status.loading .llm-status-dot {
            background: var(--bp-warning);
            animation: pulse 1.5s infinite;
        }

        .local-llm-status.ready .llm-status-dot {
            background: var(--bp-success);
        }

        .local-llm-status.error .llm-status-dot {
            background: var(--bp-error);
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.4; }
        }

        /* Extraction Progress */
        .extraction-progress {
            display: flex;
            flex-direction: column;
            gap: 12px;
            padding: 16px;
            background: var(--bp-surface);
            border-radius: 12px;
            border: 1px solid var(--bp-border);
        }

        .extraction-progress-bar {
            height: 6px;
            background: var(--bp-border);
            border-radius: 3px;
            overflow: hidden;
        }

        .extraction-progress-fill {
            height: 100%;
            background: linear-gradient(90deg, var(--bp-primary), var(--bp-accent));
            border-radius: 3px;
            transition: width 0.3s ease;
        }

        .extraction-stats {
            display: flex;
            justify-content: space-between;
            font-size: 13px;
            color: var(--bp-text-muted);
        }

        /* Instant Feedback Card */
        .instant-feedback-card {
            background: linear-gradient(135deg, var(--bp-primary-bg), var(--bp-accent-bg));
            border-radius: 16px;
            padding: 24px;
            text-align: center;
            animation: fadeInUp 0.5s ease;
        }

        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .instant-feedback-card h3 {
            margin: 0 0 16px 0;
            font-size: 20px;
            color: var(--bp-text);
        }

        .detected-info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
            gap: 16px;
            margin-top: 16px;
        }

        .detected-info-item {
            background: var(--bp-surface);
            border-radius: 12px;
            padding: 16px;
            text-align: center;
        }

        .detected-info-item .label {
            font-size: 12px;
            color: var(--bp-text-muted);
            margin-bottom: 4px;
        }

        .detected-info-item .value {
            font-size: 18px;
            font-weight: 600;
            color: var(--bp-text);
        }

        .detected-info-item .confidence {
            font-size: 11px;
            color: var(--bp-success);
            margin-top: 4px;
        }
        """

    @staticmethod
    def get_html() -> str:
        """HTML fuer Local LLM UI-Elemente (wird in Wizard eingebettet)."""
        return """
        <!-- Local LLM Status (oben im Wizard) -->
        <div id="local-llm-status" class="local-llm-status" style="display: none;">
            <span class="llm-status-dot"></span>
            <span class="llm-status-text">KI-Modell wird geladen...</span>
        </div>

        <!-- Instant Feedback (nach Upload) -->
        <div id="instant-feedback" class="instant-feedback-card" style="display: none;">
            <h3>Automatisch erkannt</h3>
            <div class="detected-info-grid">
                <div class="detected-info-item">
                    <div class="label">Klasse</div>
                    <div class="value" id="detected-class">-</div>
                    <div class="confidence" id="detected-class-conf"></div>
                </div>
                <div class="detected-info-item">
                    <div class="label">Fach</div>
                    <div class="value" id="detected-subject">-</div>
                    <div class="confidence" id="detected-subject-conf"></div>
                </div>
                <div class="detected-info-item">
                    <div class="label">Schueler</div>
                    <div class="value" id="detected-count">-</div>
                </div>
                <div class="detected-info-item">
                    <div class="label">Datum</div>
                    <div class="value" id="detected-date">-</div>
                </div>
            </div>
        </div>

        <!-- Extraction Progress -->
        <div id="extraction-progress" class="extraction-progress" style="display: none;">
            <div class="extraction-progress-bar">
                <div class="extraction-progress-fill" id="extraction-fill" style="width: 0%"></div>
            </div>
            <div class="extraction-stats">
                <span id="extraction-current">0 / 0 analysiert</span>
                <span id="extraction-time">~0 Sek verbleibend</span>
            </div>
        </div>
        """

    @staticmethod
    def get_js() -> str:
        """JavaScript fuer Transformers.js + ONNX Integration."""
        return """
        // ============================================================
        // LOCAL LLM - Transformers.js + ONNX Header Extraction
        // ============================================================

        // Konfiguration
        const LOCAL_LLM_CONFIG = {
            // Transformers.js CDN
            transformersUrl: 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.1',

            // Modell fuer Header-Extraktion (Vision + Text)
            // Option 1: Florence-2 (Microsoft) - gut fuer OCR + Strukturerkennung
            // Option 2: PaddleOCR via ONNX - spezialisiert auf Handschrift
            // Option 3: Custom fine-tuned Modell
            modelId: 'Xenova/florence-2-base',  // Fallback: Xenova/vit-gpt2-image-captioning

            // Alternative: Lokaler OCR-basierter Ansatz
            useOcrFallback: true,

            // Cache
            cacheName: 'breakpilot-llm-v1',

            // Performance
            maxParallelExtractions: 4,
            headerRegionPercent: 0.20  // Top 20% der Seite
        };

        // State
        let localLLMState = {
            isLoading: false,
            isReady: false,
            error: null,
            pipeline: null,
            ocrWorker: null
        };

        // ============================================================
        // INITIALIZATION
        // ============================================================

        /**
         * Initialisiert das lokale LLM (Transformers.js).
         * Wird beim ersten Magic-Onboarding-Start aufgerufen.
         */
        async function initLocalLLM() {
            if (localLLMState.isReady) return true;
            if (localLLMState.isLoading) {
                // Warten bis geladen
                return new Promise((resolve) => {
                    const checkReady = setInterval(() => {
                        if (localLLMState.isReady) {
                            clearInterval(checkReady);
                            resolve(true);
                        }
                    }, 100);
                });
            }

            localLLMState.isLoading = true;
            updateLLMStatus('loading', 'KI-Modell wird geladen...');

            try {
                // Transformers.js dynamisch laden
                if (!window.Transformers) {
                    await loadScript(LOCAL_LLM_CONFIG.transformersUrl);
                }

                // OCR Worker initialisieren (Tesseract.js als Fallback)
                if (LOCAL_LLM_CONFIG.useOcrFallback) {
                    await initOCRWorker();
                }

                localLLMState.isReady = true;
                localLLMState.isLoading = false;
                updateLLMStatus('ready', 'KI bereit');

                console.log('[LocalLLM] Initialisierung abgeschlossen');
                return true;

            } catch (error) {
                console.error('[LocalLLM] Fehler bei Initialisierung:', error);
                localLLMState.error = error;
                localLLMState.isLoading = false;
                updateLLMStatus('error', 'KI-Fehler: ' + error.message);
                return false;
            }
        }

        /**
         * Initialisiert Tesseract.js als OCR-Fallback.
         */
        async function initOCRWorker() {
            // Tesseract.js fuer deutsche Handschrifterkennung
            if (!window.Tesseract) {
                await loadScript('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js');
            }

            localLLMState.ocrWorker = await Tesseract.createWorker('deu', 1, {
                logger: m => {
                    if (m.status === 'recognizing text') {
                        updateExtractionProgress(m.progress * 100);
                    }
                }
            });

            console.log('[LocalLLM] OCR Worker initialisiert (Deutsch)');
        }

        // ============================================================
        // HEADER EXTRACTION
        // ============================================================

        /**
         * Extrahiert Header-Daten aus mehreren Bildern.
         * @param {File[]} files - Array von Bild-Dateien
         * @returns {Promise<ExtractionResult>}
         */
        async function extractExamHeaders(files) {
            if (!localLLMState.isReady) {
                await initLocalLLM();
            }

            const startTime = Date.now();
            showExtractionProgress(true);

            const results = {
                students: [],
                detectedClass: null,
                detectedSubject: null,
                detectedDate: null,
                classConfidence: 0,
                subjectConfidence: 0,
                errors: []
            };

            // Parallele Verarbeitung mit Limit
            const batchSize = LOCAL_LLM_CONFIG.maxParallelExtractions;

            for (let i = 0; i < files.length; i += batchSize) {
                const batch = files.slice(i, Math.min(i + batchSize, files.length));

                const batchResults = await Promise.all(
                    batch.map(file => extractSingleHeader(file))
                );

                // Ergebnisse aggregieren
                for (const result of batchResults) {
                    if (result.error) {
                        results.errors.push(result.error);
                        continue;
                    }

                    if (result.studentName) {
                        results.students.push({
                            firstName: result.firstName || result.studentName,
                            lastNameHint: result.lastNameHint,
                            fullName: result.studentName,
                            confidence: result.nameConfidence
                        });
                    }

                    // Klasse/Fach/Datum aggregieren (hoechste Konfidenz gewinnt)
                    if (result.className && result.classConfidence > results.classConfidence) {
                        results.detectedClass = result.className;
                        results.classConfidence = result.classConfidence;
                    }
                    if (result.subject && result.subjectConfidence > results.subjectConfidence) {
                        results.detectedSubject = result.subject;
                        results.subjectConfidence = result.subjectConfidence;
                    }
                    if (result.date && !results.detectedDate) {
                        results.detectedDate = result.date;
                    }
                }

                // Progress Update
                const progress = Math.min(100, ((i + batch.length) / files.length) * 100);
                const elapsed = (Date.now() - startTime) / 1000;
                const remaining = (elapsed / (i + batch.length)) * (files.length - i - batch.length);

                updateExtractionProgress(progress, i + batch.length, files.length, remaining);
            }

            showExtractionProgress(false);
            return results;
        }

        /**
         * Extrahiert Header-Daten aus einem einzelnen Bild.
         */
        async function extractSingleHeader(file) {
            try {
                // 1. Bild laden
                const img = await loadImageFromFile(file);

                // 2. Header-Region extrahieren (Top 20%)
                const headerCanvas = extractHeaderRegion(img);

                // 3. OCR auf Header-Region
                const ocrResult = await performOCR(headerCanvas);

                // 4. Strukturierte Daten extrahieren
                const parsed = parseHeaderText(ocrResult.text);

                return {
                    ...parsed,
                    nameConfidence: ocrResult.confidence,
                    rawText: ocrResult.text
                };

            } catch (error) {
                console.error('[LocalLLM] Fehler bei Extraktion:', error);
                return { error: error.message };
            }
        }

        /**
         * Laedt ein Bild aus einer File.
         */
        function loadImageFromFile(file) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(img);
                img.onerror = reject;
                img.src = URL.createObjectURL(file);
            });
        }

        /**
         * Extrahiert die Header-Region (Top X%) aus einem Bild.
         */
        function extractHeaderRegion(img) {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            const headerHeight = Math.floor(img.height * LOCAL_LLM_CONFIG.headerRegionPercent);

            canvas.width = img.width;
            canvas.height = headerHeight;

            ctx.drawImage(img, 0, 0, img.width, headerHeight, 0, 0, img.width, headerHeight);

            return canvas;
        }

        /**
         * Fuehrt OCR auf einem Canvas aus.
         */
        async function performOCR(canvas) {
            if (!localLLMState.ocrWorker) {
                throw new Error('OCR Worker nicht initialisiert');
            }

            const { data } = await localLLMState.ocrWorker.recognize(canvas);

            return {
                text: data.text,
                confidence: data.confidence / 100,
                words: data.words
            };
        }

        /**
         * Parst OCR-Text und extrahiert strukturierte Daten.
         */
        function parseHeaderText(text) {
            const result = {
                studentName: null,
                firstName: null,
                lastNameHint: null,
                className: null,
                classConfidence: 0,
                subject: null,
                subjectConfidence: 0,
                date: null
            };

            const lines = text.split('\\n').map(l => l.trim()).filter(l => l);

            for (const line of lines) {
                // Klassenname erkennen (z.B. "3a", "Klasse 10b", "Q1")
                const classMatch = line.match(/\\b(Klasse\\s*)?(\\d{1,2}[a-zA-Z]?|Q[12]|E[1-2]|EF|[KG]\\d)\\b/i);
                if (classMatch) {
                    result.className = classMatch[2] || classMatch[0];
                    result.classConfidence = 0.9;
                }

                // Fach erkennen
                const subjects = {
                    'mathe': 'Mathematik',
                    'mathematik': 'Mathematik',
                    'deutsch': 'Deutsch',
                    'englisch': 'Englisch',
                    'english': 'Englisch',
                    'physik': 'Physik',
                    'chemie': 'Chemie',
                    'biologie': 'Biologie',
                    'bio': 'Biologie',
                    'geschichte': 'Geschichte',
                    'erdkunde': 'Erdkunde',
                    'geographie': 'Geographie',
                    'politik': 'Politik',
                    'kunst': 'Kunst',
                    'musik': 'Musik',
                    'sport': 'Sport',
                    'religion': 'Religion',
                    'ethik': 'Ethik',
                    'informatik': 'Informatik',
                    'latein': 'Latein',
                    'franzoesisch': 'Franzoesisch',
                    'französisch': 'Franzoesisch',
                    'spanisch': 'Spanisch'
                };

                const lowerLine = line.toLowerCase();
                for (const [key, value] of Object.entries(subjects)) {
                    if (lowerLine.includes(key)) {
                        result.subject = value;
                        result.subjectConfidence = 0.95;
                        break;
                    }
                }

                // Datum erkennen (verschiedene Formate)
                const datePatterns = [
                    /\\b(\\d{1,2})\\.(\\d{1,2})\\.(\\d{2,4})\\b/,  // 12.01.2026
                    /\\b(\\d{1,2})\\s+(Januar|Februar|Maerz|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)\\s+(\\d{4})\\b/i
                ];

                for (const pattern of datePatterns) {
                    const dateMatch = line.match(pattern);
                    if (dateMatch) {
                        result.date = dateMatch[0];
                        break;
                    }
                }

                // Name erkennen (erste Zeile mit "Name:" oder alleinstehender Name)
                if (line.toLowerCase().includes('name')) {
                    const nameMatch = line.match(/name[:\\s]+(.+)/i);
                    if (nameMatch) {
                        result.studentName = nameMatch[1].trim();
                        parseStudentName(result);
                    }
                } else if (!result.studentName && lines.indexOf(line) < 3) {
                    // Erste Zeilen koennen Namen sein
                    const potentialName = line.replace(/[^a-zA-ZaeoeueAeOeUess\\s.-]/g, '').trim();
                    if (potentialName.length >= 2 && potentialName.length <= 40) {
                        // Pruefen ob es wie ein Name aussieht (Gross-/Kleinschreibung)
                        if (/^[A-ZAEOEUE][a-zaeoeue]+/.test(potentialName)) {
                            result.studentName = potentialName;
                            parseStudentName(result);
                        }
                    }
                }
            }

            return result;
        }

        /**
         * Parst einen Schuelernamen in Vor- und Nachname.
         */
        function parseStudentName(result) {
            if (!result.studentName) return;

            const name = result.studentName.trim();
            const parts = name.split(/\\s+/);

            if (parts.length === 1) {
                // Nur Vorname
                result.firstName = parts[0];
            } else if (parts.length === 2) {
                // Vorname Nachname oder Nachname, Vorname
                if (parts[0].endsWith(',')) {
                    result.firstName = parts[1];
                    result.lastNameHint = parts[0].replace(',', '');
                } else {
                    result.firstName = parts[0];
                    result.lastNameHint = parts[1];
                }
            } else {
                // Mehrere Teile - erster ist Vorname
                result.firstName = parts[0];
                result.lastNameHint = parts.slice(1).join(' ');
            }

            // Abkuerzungen erkennen (z.B. "M." fuer Nachname)
            if (result.lastNameHint && result.lastNameHint.match(/^[A-Z]\\.?$/)) {
                result.lastNameHint = result.lastNameHint.replace('.', '') + '.';
            }
        }

        // ============================================================
        // UI HELPERS
        // ============================================================

        /**
         * Aktualisiert den LLM-Status-Indikator.
         */
        function updateLLMStatus(state, text) {
            const statusEl = document.getElementById('local-llm-status');
            if (!statusEl) return;

            statusEl.style.display = 'flex';
            statusEl.className = 'local-llm-status ' + state;
            statusEl.querySelector('.llm-status-text').textContent = text;
        }

        /**
         * Zeigt das Instant-Feedback-Panel.
         */
        function showInstantFeedback(data) {
            const feedbackEl = document.getElementById('instant-feedback');
            if (!feedbackEl) return;

            feedbackEl.style.display = 'block';

            document.getElementById('detected-class').textContent = data.detectedClass || '-';
            document.getElementById('detected-subject').textContent = data.detectedSubject || '-';
            document.getElementById('detected-count').textContent = data.studentCount || '0';
            document.getElementById('detected-date').textContent = data.detectedDate || '-';

            // Konfidenz anzeigen
            if (data.classConfidence) {
                document.getElementById('detected-class-conf').textContent =
                    Math.round(data.classConfidence * 100) + '% sicher';
            }
            if (data.subjectConfidence) {
                document.getElementById('detected-subject-conf').textContent =
                    Math.round(data.subjectConfidence * 100) + '% sicher';
            }
        }

        /**
         * Zeigt/versteckt den Extraktions-Fortschritt.
         */
        function showExtractionProgress(show) {
            const progressEl = document.getElementById('extraction-progress');
            if (progressEl) {
                progressEl.style.display = show ? 'flex' : 'none';
            }
        }

        /**
         * Aktualisiert den Extraktions-Fortschritt.
         */
        function updateExtractionProgress(percent, current, total, remainingSec) {
            const fillEl = document.getElementById('extraction-fill');
            const currentEl = document.getElementById('extraction-current');
            const timeEl = document.getElementById('extraction-time');

            if (fillEl) fillEl.style.width = percent + '%';
            if (currentEl && current !== undefined) {
                currentEl.textContent = current + ' / ' + total + ' analysiert';
            }
            if (timeEl && remainingSec !== undefined) {
                timeEl.textContent = '~' + Math.ceil(remainingSec) + ' Sek verbleibend';
            }
        }

        /**
         * Laedt ein Script dynamisch.
         */
        function loadScript(url) {
            return new Promise((resolve, reject) => {
                const script = document.createElement('script');
                script.src = url;
                script.onload = resolve;
                script.onerror = reject;
                document.head.appendChild(script);
            });
        }

        // ============================================================
        // MAGIC ONBOARDING INTEGRATION
        // ============================================================

        /**
         * Hauptfunktion fuer Magic-Analyse beim Upload.
         * Wird von klausur_korrektur.py aufgerufen.
         */
        async function magicAnalyzeExams(files) {
            console.log('[MagicOnboarding] Starte Analyse von', files.length, 'Dateien');

            // 1. LLM initialisieren
            await initLocalLLM();

            // 2. Quick Preview (erste 5 Dateien)
            const quickFiles = files.slice(0, 5);
            const quickResults = await extractExamHeaders(quickFiles);

            // 3. Sofort Feedback zeigen (WOW-Effekt!)
            showInstantFeedback({
                detectedClass: quickResults.detectedClass,
                detectedSubject: quickResults.detectedSubject,
                studentCount: files.length,
                detectedDate: quickResults.detectedDate,
                classConfidence: quickResults.classConfidence,
                subjectConfidence: quickResults.subjectConfidence
            });

            // 4. Rest im Hintergrund verarbeiten
            if (files.length > 5) {
                const remainingFiles = files.slice(5);
                const remainingResults = await extractExamHeaders(remainingFiles);

                // Ergebnisse mergen
                quickResults.students = [
                    ...quickResults.students,
                    ...remainingResults.students
                ];
            }

            console.log('[MagicOnboarding] Analyse abgeschlossen:', quickResults);
            return quickResults;
        }

        // Export fuer globalen Zugriff
        window.LocalLLM = {
            init: initLocalLLM,
            extractHeaders: extractExamHeaders,
            magicAnalyze: magicAnalyzeExams,
            getStatus: () => localLLMState
        };
        """
