from django.db import transaction
from django.utils import timezone
from django.conf import settings
from .models import (
    Dossier, Proforma, Facture, FactureM2, FactureM3, Paiement,
    EcritureComptable, Client, LigneDossier, LigneProforma, Societe
)
from dgi.edef_client import EdefClient, EdefClientError
import logging
import time

logger = logging.getLogger(__name__)


class BillingService:

    # =========================================================
    # CALCUL DES TOTAUX PROFORMA (nouveau flux)
    # =========================================================
    @staticmethod
    def calculate_proforma_totals(proforma: Proforma):
        from decimal import Decimal
        totals = {
            'USD': {'total': Decimal('0'), 'tva': Decimal('0'), 'total_ttc': Decimal('0')},
            'FC':  {'total': Decimal('0'), 'tva': Decimal('0'), 'total_ttc': Decimal('0')},
        }

        for ligne in proforma.lignes.all():
            devise = ligne.devise
            if devise not in totals:
                totals[devise] = {'total': Decimal('0'), 'tva': Decimal('0'), 'total_ttc': Decimal('0')}
            # Sommation du Total HT par devise
            totals[devise]['total'] += ligne.montant

        # TVA 16% calculée sur le Total HT (pas par ligne)
        for dev in totals:
            totals[dev]['tva'] = totals[dev]['total'] * Decimal('0.16')
            totals[dev]['total_ttc'] = totals[dev]['total'] + totals[dev]['tva']

        return totals

    # =========================================================
    # CALCUL DES TOTAUX DOSSIER
    # =========================================================
    @staticmethod
    def calculate_dossier_totals(dossier: Dossier):
        from decimal import Decimal
        totals = {
            'USD': {'frais': 0, 'debours': 0, 'total': 0, 'tva': 0, 'total_ttc': 0},
            'FC': {'frais': 0, 'debours': 0, 'total': 0, 'tva': 0, 'total_ttc': 0}
        }

        for ligne in dossier.lignes.all():
            devise = ligne.devise
            if not ligne.service or not ligne.service.type_service:
                continue
            
            if devise not in totals:
                totals[devise] = {'frais': 0, 'debours': 0, 'total': 0, 'tva': 0, 'total_ttc': 0}

            totals[devise]['total'] += ligne.montant # This is Total HT

            # Calcul TVA (16% si code B)
            if ligne.service.taxgroup and ligne.service.taxgroup.code == 'B':
                totals[devise]['tva'] += ligne.montant * Decimal('0.16')

            type_code = ligne.service.type_service.code.lower()
            if type_code in totals[devise]:
                totals[devise][type_code] += ligne.montant
            else:
                # Ajout dynamique pour les autres types (ex: honoraires)
                totals[devise][type_code] = totals[devise].get(type_code, 0) + ligne.montant

        # Calcul du TTC
        for dev in totals:
            totals[dev]['total_ttc'] = totals[dev]['total'] + totals[dev]['tva']

        return totals

    # =========================================================
    # CRÉATION PROFORMA
    # =========================================================
    @staticmethod
    @transaction.atomic
    def create_proforma(dossier: Dossier, user=None) -> Proforma:
        if dossier.statut == 'CLOTURE':
            raise ValueError("Impossible de créer une proforma pour un dossier clôturé.")

        return Proforma.objects.create(
            dossier=dossier,
            statut='BROUILLON',
            date_emission=timezone.now(),
            created_by=user
        )

    # =========================================================
    # VALIDATION PAR IMPORT FICHIER DGI (BYPASS API)
    # =========================================================
    @staticmethod
    @transaction.atomic
    def convert_proforma_to_invoice_from_upload(proforma, uploaded_file, user=None):
        """
        Crée une facture à partir d'une proforma et des données DGI extraites d'un fichier uploadé.
        Bypasse l'appel API DGI car le fichier prouve l'existence de la facture.
        """
        import os
        from django.core.files.storage import default_storage
        from django.core.files.base import ContentFile
        from extratfile.extractor.normalized_invoice_extractor import extract_normalized_invoice
        
        # 1. Sauvegarde temporaire pour extraction
        path = default_storage.save(f"tmp/{uploaded_file.name}", ContentFile(uploaded_file.read()))
        full_path = os.path.join(settings.MEDIA_ROOT, path)
        
        try:
            # 2. Extraction des données
            data = extract_normalized_invoice(full_path)
            
            # --- DEBUG : Affichage des données extraites ---
            print("\n" + "="*50)
            print("🔍 DONNÉES DGI EXTRAITES DU FICHIER :")
            print(f"Numéro Facture (detecté) : {data.get('invoice_number')}")
            print(f"Code DGI      : {data.get('code_def_dgi')}")
            print(f"NIM           : {data.get('def_nid')}")
            print(f"Compteurs     : {data.get('def_compteurs')}")
            print(f"Date Heure    : {data.get('def_datetime')}")
            print(f"QR Code (Raw) : {data.get('qr_raw')}")
            print("="*50 + "\n")
            
            # 3. Création de la facture locale
            facture = Facture(
                dossier=proforma.dossier,
                proforma=proforma,
                date_emission=timezone.now(),
                created_by=user,
                type_facture=proforma.type_proforma,
            )
            facture.save()

            # 4. Enrichissement avec données DGI extraites
            if data.get('code_def_dgi'):
                facture.codedefdgi = data.get('code_def_dgi')
            if data.get('def_nid'):
                facture.nim = data.get('def_nid')
            if data.get('def_compteurs'):
                facture.counters = data.get('def_compteurs')
            if data.get('def_datetime'):
                facture.dateTimedgi = data.get('def_datetime')
            
            # QR Code
            qr_data = data.get('qr_raw')
            if qr_data:
                facture.qrCode = qr_data
            
            facture.save()

            # 5. Mise à jour Proforma
            proforma.statut = 'VALIDEE'
            proforma.save()

            # 6. Écritures comptables
            client_obj = proforma.get_client()
            totals = BillingService.calculate_proforma_totals(proforma)
            total_usd = totals['USD']['total_ttc']

            if total_usd > 0 and client_obj:
                BillingService.create_ecriture_comptable(
                    client=client_obj,
                    montant=total_usd,
                    sens='DEBIT',
                    libelle=f"Facture N° {facture.numero} (Import DGI)",
                    facture=facture
                )
            
            logger.info(f"Facture {facture.numero} créée via import DGI pour Proforma {proforma.numero}")
            return facture

        finally:
            # Nettoyage
            if os.path.exists(full_path):
                os.remove(full_path)

    # =========================================================
    # VALIDATION PROFORMA → FACTURE + DGI
    # =========================================================
    @staticmethod
    @transaction.atomic
    def validate_proforma(proforma: Proforma, user=None) -> Facture:
        if proforma.statut != 'BROUILLON':
            raise ValueError("Seule une proforma brouillon peut être validée.")

        # 1️⃣ Mise à jour statut proforma
        proforma.statut = 'VALIDEE'
        proforma.save()

        # Client : nouveau flux (proforma.client) ou ancien (proforma.dossier.client)
        client_obj = proforma.get_client()
        if not client_obj:
            raise ValueError("La proforma n'a pas de client associé.")

        # Mise à jour dossier si présent (compat)
        if proforma.dossier:
            proforma.dossier.statut = 'FACTURE'
            proforma.dossier.save()

        # 2️⃣ Création facture locale
        facture = Facture.objects.create(
            proforma=proforma,
            dossier=proforma.dossier,
            date_emission=timezone.now(),
            created_by=user,
            type_facture=proforma.type_proforma,
        )

        # Lignes : nouveau flux (LigneProforma) ou ancien (LigneDossier via dossier)
        lignes_proforma = list(proforma.lignes.all())
        if lignes_proforma:
            lignes = lignes_proforma
            use_new_model = True
        else:
            lignes = list(proforma.dossier.lignes.all()) if proforma.dossier else []
            use_new_model = False

        societe = Societe.objects.first()
        client_api = EdefClient()

        try:
            # =====================================================
            # 3️⃣ CONSTRUCTION DES ITEMS DGI
            # =====================================================
            items = []
            total_amount = 0.0

            for ligne in lignes:
                # item_type et tax_group sont fixes pour les services
                item_type = "SER"
                tax_group = "B"  # 16% TVA groupe B

                # Utiliser le montant en Francs Congolais pour la DGI
                prix_fc = float(ligne.prix_unitaire_fc) if use_new_model else float(ligne.montant)
                montant_fc = float(ligne.montant_fc) if use_new_model else float(ligne.montant)

                # code : utiliser le code du service si disponible, sinon "SRV"
                code_item = "SRV"
                if not use_new_model and hasattr(ligne, 'service') and ligne.service:
                    code_item = ligne.service.code

                items.append({
                    "code": code_item,
                    "name": ligne.designation if use_new_model else getattr(ligne.service, 'designation', ligne.designation),
                    "type": item_type,
                    "quantity": float(ligne.quantite) if use_new_model else 1,
                    "price": prix_fc,
                    "taxGroup": tax_group
                })

                total_amount += montant_fc
            
            print("--- Items à facturer ---")
            print(f"Total montant à facturer (FC) : {total_amount}")
            print(f"Items à facturer : {items}")

            # =====================================================
            # 4️⃣ DEVISE (FC par défaut pour les montants DGI)
            # =====================================================
            currency_code = "CDF"  # Franc Congolais
            # Utiliser le taux de la société comme taux de conversion USD→CDF
            cur_rate = float(societe.taux) if societe and societe.taux else 2800.0
            cur_date = timezone.now().strftime("%Y-%m-%d %H:%M:%S")

            print(f"--- Devise et Taux ---")
            print(f"Devise : {currency_code}, Taux : {cur_rate}, Date : {cur_date}")

            # =====================================================
            # 5️⃣ CLIENT DGI (conforme e-MCF)
            # =====================================================
            client_type_code = "PM"
            if hasattr(client_obj, 'client_type') and client_obj.client_type:
                client_type_code = client_obj.client_type.code

            client_dgi = {
                "name": getattr(client_obj, 'name', None) or client_obj.nom or "CLIENT DIVERS",
                "type": client_type_code,
                "address": getattr(client_obj, "address", None) or getattr(client_obj, 'adresse', ''),
                "contact": getattr(client_obj, "contact", None) or getattr(client_obj, 'telephone', '')
            }

            if getattr(client_obj, "nif", None):
                client_dgi["nif"] = client_obj.nif

            # =====================================================
            # 6️⃣ PAYLOAD FACTURE FV
            # =====================================================
            payload = {
                "nif": societe.nif if societe else settings.DGI_NIF,
                "rn": f"FV-{facture.id}-{int(time.time())}",
                "mode": "ht",
                "isf": societe.isf_dgi if societe else settings.DGI_ISF,
                "type": "FV",
                "items": items,
                "client": client_dgi,
                "payment": [{
                    "name": "ESPECES",
                    "amount": total_amount  # Montant total en FC
                }],
                "curCode": currency_code,
                "curRate": cur_rate,
                "curDate": cur_date,
                "operator": {
                    "id": str(user.id) if user and user.is_authenticated else "01",
                    "name": user.username if user and user.is_authenticated else "SYSTEM"
                }
            }

            # =====================================================
            # 7️⃣ CREATE INVOICE DGI
            # =====================================================
            resp_create = client_api.create_invoice(payload)
            uid = resp_create.get("uid")
            
            if not uid:
                raise EdefClientError(f"CREATE DGI échoué : {resp_create}")
            
            # Utilisation directe de la réponse de création qui contient déjà les totaux calculés
            # au lieu de rappeler get_invoice(uid) qui semble poser problème.
            invoice_data = resp_create 

            # =====================================================
            # 8️⃣ CONFIRM INVOICE DGI (OBLIGATOIRE)
            # =====================================================
            # On confirme avec les totaux calculés par la DGI
            resp_confirm = client_api.confirm_invoice(uid, {
                "total": invoice_data.get("total"),
                "vtotal": invoice_data.get("vtotal", 0) # Montant TVA total si applicable
            })

            # =====================================================
            # 9️⃣ SAUVEGARDE DES DONNÉES DGI
            # =====================================================
            facture.uid = uid
            facture.codedefdgi = resp_confirm.get("codeDEFDGI") # Peut varier selon version API (mtn, code, etc)
            if not facture.codedefdgi: facture.codedefdgi = resp_confirm.get("mtn")
            
            facture.qrCode = resp_confirm.get("qrCode")
            facture.nim = resp_confirm.get("nim")
            facture.counters = resp_confirm.get("counters")
            facture.dateTimedgi = resp_confirm.get("dateTime")
            facture.save()

            logger.info(f"Facture {facture.numero} normalisée DGI avec succès")

        except Exception as e:
            # =====================================================
            # � ERREUR DGI — annulation complète (rollback transaction)
            # =====================================================
            logger.error(f"[ERREUR DGI] {str(e)}")
            raise  # Propage l'exception → @transaction.atomic rollback tout

        # =====================================================
        # 🔟 ÉCRITURES COMPTABLES
        # =====================================================
        totals = BillingService.calculate_proforma_totals(proforma)
        total_usd = totals['USD']['total_ttc']

        if total_usd > 0:
            BillingService.create_ecriture_comptable(
                client=client_obj,
                montant=total_usd,
                sens='DEBIT',
                libelle=f"Facture N° {facture.numero}",
                facture=facture
            )

        return facture

    # =========================================================
    # ENREGISTREMENT PAIEMENT
    # =========================================================
    @staticmethod
    @transaction.atomic
    def register_payment(client: Client, montant, devise, reference, facture: Facture = None):
        paiement = Paiement.objects.create(
            client=client,
            facture=facture,
            montant=montant,
            devise=devise,
            reference=reference,
            date_paiement=timezone.now()
        )

        BillingService.create_ecriture_comptable(
            client=client,
            montant=montant,
            sens='CREDIT',
            libelle=f"Paiement {reference}",
            paiement=paiement
        )

        return paiement

    # =========================================================
    # ÉCRITURE COMPTABLE
    # =========================================================
    @staticmethod
    def create_ecriture_comptable(client, montant, sens, libelle, facture=None, paiement=None):
        ecriture = EcritureComptable.objects.create(
            client=client,
            montant=montant,
            sens=sens,
            libelle=libelle,
            facture_liee=facture,
            paiement_lie=paiement
        )

        if sens == 'DEBIT':
            client.solde_compte += montant
        else:
            client.solde_compte -= montant

        client.save()
        return ecriture

    # =========================================================
    # VALIDATION PROFORMA M2 → FACTURE M2 (UPLOAD DGI)
    # =========================================================
    @staticmethod
    @transaction.atomic
    def convert_proforma_m2_to_facture_from_upload(proforma, uploaded_file, user=None):
        import os
        from django.core.files.storage import default_storage
        from django.core.files.base import ContentFile
        from extratfile.extractor.normalized_invoice_extractor import extract_normalized_invoice

        path = default_storage.save(f"tmp/{uploaded_file.name}", ContentFile(uploaded_file.read()))
        full_path = os.path.join(settings.MEDIA_ROOT, path)

        try:
            data = extract_normalized_invoice(full_path)

            facture = FactureM2(
                proforma=proforma,
                date_emission=timezone.now(),
                created_by=user,
                type_facture=proforma.type_proforma,
            )
            facture.save()

            if data.get('code_def_dgi'):
                facture.codedefdgi = data.get('code_def_dgi')
            if data.get('def_nid'):
                facture.nim = data.get('def_nid')
            if data.get('def_compteurs'):
                facture.counters = data.get('def_compteurs')
            if data.get('def_datetime'):
                facture.dateTimedgi = data.get('def_datetime')
            if data.get('qr_raw'):
                facture.qrCode = data.get('qr_raw')
            facture.save()

            proforma.statut = 'VALIDEE'
            proforma.save()

            logger.info(f"FactureM2 {facture.numero} créée via import DGI pour Proforma {proforma.numero}")
            return facture

        finally:
            if os.path.exists(full_path):
                os.remove(full_path)

    # =========================================================
    # VALIDATION PROFORMA M3 → FACTURE M3 (UPLOAD DGI)
    # =========================================================
    @staticmethod
    @transaction.atomic
    def convert_proforma_m3_to_facture_from_upload(proforma, uploaded_file, user=None):
        import os
        from django.core.files.storage import default_storage
        from django.core.files.base import ContentFile
        from extratfile.extractor.normalized_invoice_extractor import extract_normalized_invoice

        path = default_storage.save(f"tmp/{uploaded_file.name}", ContentFile(uploaded_file.read()))
        full_path = os.path.join(settings.MEDIA_ROOT, path)

        try:
            data = extract_normalized_invoice(full_path)

            facture = FactureM3(
                proforma=proforma,
                date_emission=timezone.now(),
                created_by=user,
                type_facture=proforma.type_proforma,
            )
            facture.save()

            if data.get('code_def_dgi'):
                facture.codedefdgi = data.get('code_def_dgi')
            if data.get('def_nid'):
                facture.nim = data.get('def_nid')
            if data.get('def_compteurs'):
                facture.counters = data.get('def_compteurs')
            if data.get('def_datetime'):
                facture.dateTimedgi = data.get('def_datetime')
            if data.get('qr_raw'):
                facture.qrCode = data.get('qr_raw')
            facture.save()

            proforma.statut = 'VALIDEE'
            proforma.save()

            logger.info(f"FactureM3 {facture.numero} créée via import DGI pour Proforma {proforma.numero}")
            return facture

        finally:
            if os.path.exists(full_path):
                os.remove(full_path)
