const axios = require('axios');
const fs = require('fs');
const sharp = require('sharp');
const path = require('path');

class OcrService {
    constructor() {
        this.apiKey = process.env.OPENROUTER_API_KEY;
        this.model = process.env.OPENROUTER_MODEL || 'google/gemini-2.5-flash';
        console.log('[OCR Service] Initialisé avec:', {
            hasApiKey: !!this.apiKey,
            apiKeyPreview: this.apiKey ? this.apiKey.substring(0, 10) + '...' : 'MANQUANT',
            model: this.model
        });
    }

    /**
     * Construit le prompt pour l'API en fonction du contexte (import ou export).
     * @param {string} context - Le contexte de l'opération ('import' ou 'export').
     * @returns {string} Le prompt formaté.
     */
    buildExtractionPrompt(context) {
        const actualContext = context || 'import';

        const basePrompt = `Tu es un expert en extraction de données de certificats d'immatriculation (cartes grises) européens.
Analyse l'image ou le document fourni et retourne UNIQUEMENT un objet JSON strict correspondant au format:
{"vin":"VF3LCYHZPFS123456","brand":"Peugeot","model":"308","year":2015,"cylinder_capacity":1560,"fuel_type":"diesel","weight_empty":1245,"weight_total":1870,"type_approval":"e112007/460009*15","paper_origin":"FRANCE","matriculation_number":null}
RÈGLES ET TERMES DE RECHERCHE PAR CHAMP:
- vin: 17 caractères alphanumériques. Cherche les champs E, 23, FIN, Chassis N., Fahrgestell-Nr, Telaio n., ou VIN. Supprime les espaces internes. IMPORTANT: Les lettres I, O et Q ne sont JAMAIS utilisées dans un VIN. Si tu rencontres un I, considère-le comme un 1. Si tu rencontres un O ou un Q, considère-les comme des 0.
- brand: D.1, Marke.
- model: D.3, Modèle, Typ.
- year: L'année de la première mise en circulation. Cherche B, 1. Inverkehrsetzung, 1ère mise en circulation, Data di prima immatricolazione. Isole UNIQUEMENT l'année (YYYY).
- cylinder_capacity: P.1, Hubraum, Cilindrata, Cylindrée. (Int en cm³)
- fuel_type: P.3, type de carburant, Carburant, Kraftstoff, Alimentazione. (essence|diesel|electrique|hybride|hybride_plugin)
- weight_empty: G, Leergewicht, Poids à vide, massa a vuoto. (Int en kg)
- weight_total: F.2, Gesamtgewicht, Poids total autorisé en charge, Massa massima ammissibile a pieno carico. (Int en kg)
- type_approval: K, Réception par type, Typengenehmigung, Approvazione del tipo.
- paper_origin: Retourne le pays de la carte grise EN FRANCAIS! Après avoir analyser Le titre du document, les inscriptions, La langue utilisée, l'adresse et la ville, la structure des identifiants, la ligne D.1 ou tout autre information contenue dans la carte grise pour déterminer le pays d'emission du document.`;

        const matriculationInstruction = `- matriculation_number: Numéro de matricule ou plaque d'immatriculation. En Suisse, cherche le point 18 (Stammnumer, N° matricule ). Format typique: XXX.XXX.XXX. Si absent ou illisible, retourne null.`;

        if (actualContext === 'import') {
            return `${basePrompt}
Pour les documents d'importation, le champ matriculation_number sera null.
Contexte: le document provient d'un véhicule importé. Retourne strictement du JSON valide sans commentaire ni texte. Si une valeur est totalement inconnue, utilise: null.`;
        } else if (actualContext === 'export') {
            return `${basePrompt}
${matriculationInstruction}
Contexte: le document provient d'un véhicule Suisse destiné à l'export. paper_origin est donc "suisse".
Retourne strictement du JSON valide sans commentaire ni texte. Si une valeur est totalement inconnue, utilise: null.`;
        }

        // Retour par défaut en cas de contexte non reconnu, bien que le code en amont assigne 'import' par défaut.
        return basePrompt;
    }

    // NOTE CORRECTION: Le bloc "if (context === 'import') { ... }" qui se trouvait ici a été supprimé.
    // Il était en dehors de toute méthode, ce qui causait une SyntaxError.
    // La logique est déjà correctement gérée dans la méthode `buildExtractionPrompt` ci-dessus.

    validateVIN(vin) {
        if (!vin) return null;
        let clean = vin.replace(/\s/g, '').toUpperCase().trim();
        const corrected = clean
            .replace(/I/g, '1')
            .replace(/[OQ]/g, '0');

        if (clean !== corrected) {
            console.warn('[OCR] VIN corrigé automatiquement:', { original: vin, corrected });
        }
        return /^[A-HJ-NPR-Z0-9]{17}$/.test(corrected) ? corrected : null;
    }

    validateYear(year) {
        if (!year) return null;
        const y = parseInt(year, 10);
        return (y >= 1900 && y <= new Date().getFullYear() + 1) ? y : null;
    }

    validateInteger(value) {
        if (value === null || value === undefined || value === '') return null;
        const int = parseInt(value, 10);
        return Number.isFinite(int) && int > 0 ? int : null;
    }

    normalizeFuelType(fuelType) {
        if (!fuelType) return null;
        const normalized = fuelType.toLowerCase().trim();
        if (normalized.includes('plug')) return 'hybride_plugin';
        if (normalized.includes('hybr')) return 'hybride';
        if (normalized.includes('elec') || normalized.includes('élec')) return 'electrique';
        if (normalized.includes('dies')) return 'diesel';
        if (normalized.includes('ess') || normalized.includes('petrol') || normalized.includes('gasoline')) return 'essence';
        return null; // Retourne null si le type n'est pas reconnu pour éviter les valeurs non standard
    }

    parseGeminiJSON(response) {
        console.log('[OCR] ÉTAPE 7a - Parsing JSON, réponse brute:', response);
        const clean = response.replace(/```json|```/g, '').trim();
        console.log('[OCR] ÉTAPE 7b - Après nettoyage:', clean);

        try {
            const data = JSON.parse(clean);
            console.log('[OCR] ÉTAPE 7c - JSON parsé:', data);

            return {
                vin: this.validateVIN(data.vin),
                brand: data.brand || null,
                model: data.model || null,
                year: this.validateYear(data.year),
                cylinder_capacity: this.validateInteger(data.cylinder_capacity),
                fuel_type: this.normalizeFuelType(data.fuel_type),
                weight_empty: this.validateInteger(data.weight_empty),
                weight_total: this.validateInteger(data.weight_total),
                type_approval: data.type_approval || null,
                // NOTE OPTIMISATION: Ajout du champ 'paper_origin' qui était demandé dans le prompt mais manquant ici.
                paper_origin: data.paper_origin || null,
                matriculation_number: data.matriculation_number || null
            };
        } catch (err) {
            console.error('[OCR] ERREUR ÉTAPE 7 - Parsing JSON:', err);
            throw new Error('Format de réponse OCR invalide: ' + err.message);
        }
    }

    async extractFromFile(filePath, mimeType, context) {
        console.log('[OCR] ÉTAPE 1 - Début extraction:', { filePath, mimeType, context });

        if (!this.apiKey) {
            throw new Error('OPENROUTER_API_KEY manquante dans .env');
        }

        const safeMime = ['image/jpeg', 'image/png', 'application/pdf'].includes(mimeType) ? mimeType : 'image/jpeg';
        console.log('[OCR] ÉTAPE 2 - Type MIME validé:', safeMime);

        let base64Data;
        let contentType;

        try {
            let buffer;
            if (safeMime === 'application/pdf') {
                console.log('[OCR] ÉTAPE 3a - Traitement PDF');
                buffer = fs.readFileSync(filePath);
                contentType = 'application/pdf';
                console.log('[OCR] PDF lu, taille:', buffer.length, 'bytes');
            } else {
                console.log('[OCR] ÉTAPE 3b - Traitement Image');
                buffer = await sharp(filePath)
                    .rotate()
                    .resize({ width: 2048, withoutEnlargement: true })
                    .jpeg({ quality: 85 })
                    .toBuffer();
                contentType = 'image/jpeg';
                console.log('[OCR] Image optimisée, taille:', buffer.length, 'bytes');
            }
            base64Data = buffer.toString('base64');
            console.log('[OCR] Fichier converti en base64, longueur:', base64Data.length);

            const dataSizeMB = base64Data.length * 0.75 / (1024 * 1024);
            console.log(`[OCR] ÉTAPE 3d - Taille du contenu à envoyer: ${dataSizeMB.toFixed(2)} MB`);
            if (dataSizeMB > 15) {
                console.warn('[OCR] AVERTISSEMENT: La taille du contenu est supérieure à 15MB, risque d\'échec API.');
            }

        } catch (error) {
            console.error('[OCR] ERREUR ÉTAPE 3 - Lecture/conversion fichier:', error);
            throw new Error('Impossible de lire ou de traiter le fichier: ' + error.message);
        }

        const prompt = this.buildExtractionPrompt(context);
        console.log('[OCR] ÉTAPE 4 - Prompt construit.');

        const dataUrl = `data:${contentType};base64,${base64Data}`;

        console.log('[OCR] ÉTAPE 5 - Envoi requête à OpenRouter');
        try {
            const response = await axios.post('https://openrouter.ai/api/v1/chat/completions', {
                model: this.model,
                messages: [{
                    role: 'user',
                    content: [
                        { type: 'text', text: prompt },
                        { type: 'image_url', image_url: { url: dataUrl } }
                    ]
                }],
                temperature: 0.1,
                max_tokens: 4096, // Augmenté pour être sûr
                response_format: { type: "json_object" } // Demande explicite de JSON si le modèle le supporte
            }, {
                headers: {
                    'Authorization': `Bearer ${this.apiKey}`,
                    'Content-Type': 'application/json',
                    'HTTP-Referer': process.env.REFERER_URL || 'http://localhost:3000',
                    'X-Title': 'e-dec Vehicles'
                },
                timeout: 45000 // Augmenté pour les fichiers plus lourds
            });

            console.log('[OCR] ÉTAPE 6 - Réponse API reçue.');
            const extractedText = response.data.choices?.[0]?.message?.content || '';
            
            if (!extractedText) {
                throw new Error("La réponse de l'API est vide.");
            }
            
            console.log('[OCR] ÉTAPE 7 - Contenu extrait (aperçu):', extractedText.substring(0, 200));
            const parsed = this.parseGeminiJSON(extractedText);
            console.log('[OCR] ÉTAPE 8 - JSON parsé et validé avec succès:', parsed);
            return parsed;

        } catch (error) {
            const status = error.response?.status || 'N/A';
            const code = error.code || 'N/A';
            const errorData = error.response?.data;

            console.error('[OCR] ERREUR CRITIQUE - Requête API:', {
                message: error.message,
                status: status,
                code: code,
                details: errorData
            });

            if (status === 401) {
                throw new Error('Clé API OpenRouter invalide ou expirée (401)');
            }
            if (status === 429) {
                throw new Error('Quota OpenRouter dépassé (429)');
            }
            if (code === 'ECONNABORTED' || error.message.includes('timeout')) {
                throw new Error('Timeout: OpenRouter n\'a pas répondu dans le temps imparti (45 secondes)');
            }
            
            const apiErrorMessage = errorData?.error?.message || error.message;
            throw new Error(`Erreur API OpenRouter inattendue: ${apiErrorMessage}`);
        }
    }
}

module.exports = new OcrService();
