"""
DOOBAT - Service IA (OpenAI)
Analyse les messages Telegram en langage naturel.
Fallback regex si OpenAI indisponible.
Copyright 2026 Vincent Gallet - Vincesoft
"""

import json
import logging
import re
from configparser import ConfigParser
from typing import Optional

logger = logging.getLogger("doobat.ai")


# ── Modèle de résultat ─────────────────────────────────────────────────────────

VALID_INTENTS = {
    "tarif", "stock", "client_documents", "supplier_documents",
    "actions", "help", "unknown",
}

DEFAULT_RESULT = {
    "intent": "unknown",
    "client_name": "",
    "supplier_name": "",
    "article_name": "",
    "confidence": 0.0,
}


# ── Service IA ─────────────────────────────────────────────────────────────────

class AIService:
    """
    Analyse les messages en langage naturel pour détecter :
    - l'intention (tarif, stock, client_documents, help, unknown)
    - le client demandé
    - l'article demandé
    - le niveau de confiance

    Utilise OpenAI GPT avec fallback regex si OpenAI est indisponible.
    """

    SYSTEM_PROMPT = """Tu es un assistant pour un bot Telegram professionnel connecté à Odoo.
Ton rôle est de comprendre les demandes naturelles des commerciaux puis de retourner un JSON structuré exploitable par le bot.

Intentions possibles :
- "tarif" : demande de liste de prix, tarif, catalogue
- "stock" : demande de stock, disponibilité, quantité d'un article, de tous les produits ou d'une catégorie
- "client_documents" : demande de documents, devis, commandes, factures d'un contact/client. Si l'utilisateur ne precise pas si c'est un client ou un fournisseur, utilise cette intention et mets le nom detecte dans "client_name".
- "supplier_documents" : demande explicite de commandes d'achat ou factures d'un fournisseur
- "actions" : demande d'actions commerciales, opportunités, relances
- "help" : demande d'aide, liste des commandes
- "unknown" : message non reconnu

Retourne UNIQUEMENT un JSON valide sans texte supplémentaire :
{
  "intent": "<intention>",
  "client_name": "<nom du client si mentionné, sinon vide>",
  "supplier_name": "<nom du fournisseur si mentionné, sinon vide>",
  "article_name": "<nom de l'article si mentionné, sinon vide>",
  "confidence": <float entre 0.0 et 1.0>
}

Exemples :
- "envoie le tarif" → {"intent": "tarif", "client_name": "", "article_name": "", "confidence": 0.99}
- "stock du produit ABC123" → {"intent": "stock", "client_name": "", "article_name": "ABC123", "confidence": 0.95}
- "recherche boite de vis" → {"intent": "stock", "client_name": "", "article_name": "boite de vis", "confidence": 0.90}
- "stock de tous les produits" → {"intent": "stock", "client_name": "", "article_name": "", "confidence": 0.95}
- "stock de la catégorie Peinture" → {"intent": "stock", "client_name": "", "article_name": "categorie: Peinture", "confidence": 0.95}
- "stock de l'électricité" → {"intent": "stock", "client_name": "", "article_name": "categorie: électricité", "confidence": 0.95}
- "je veux le stock de l'électricité" → {"intent": "stock", "client_name": "", "article_name": "categorie: électricité", "confidence": 0.95}
- "factures du client DUPONT" → {"intent": "client_documents", "client_name": "DUPONT", "article_name": "", "confidence": 0.97}
- "commandes de Martin SAS" → {"intent": "client_documents", "client_name": "Martin SAS", "article_name": "", "confidence": 0.95}
- "documents de Corps Logistique" → {"intent": "client_documents", "client_name": "Corps Logistique", "supplier_name": "", "article_name": "", "confidence": 0.95}
- "factures fournisseur ACME" → {"intent": "supplier_documents", "supplier_name": "ACME", "client_name": "", "article_name": "", "confidence": 0.95}
- "aide" → {"intent": "help", "client_name": "", "article_name": "", "confidence": 0.99}

"""

    def __init__(self, config: ConfigParser):
        self._api_key = config.get("OPENAI", "api_key", fallback="")
        self._model = config.get("OPENAI", "model", fallback="gpt-4o-mini")
        self._max_tokens = config.getint("OPENAI", "max_tokens", fallback=500)
        self._temperature = config.getfloat("OPENAI", "temperature", fallback=0.1)
        self._timeout = config.getint("OPENAI", "timeout", fallback=20)
        self._client = None

        if self._api_key and self._api_key.startswith("sk-"):
            try:
                from openai import OpenAI
                self._client = OpenAI(api_key=self._api_key, timeout=self._timeout)
                logger.info(f"OpenAI initialisé avec modèle {self._model}")
            except ImportError:
                logger.warning("openai non installé — fallback regex uniquement")
        else:
            logger.warning("Clé OpenAI manquante ou invalide — fallback regex activé")

    # ── Analyse principale ─────────────────────────────────────────────────────

    def analyze_message(self, message: str) -> dict:
        """
        Analyse un message Telegram en langage naturel.
        Retourne un dict avec intent, client_name, article_name, confidence.
        Utilise OpenAI si disponible, sinon fallback regex.
        """
        if not message or not message.strip():
            return {**DEFAULT_RESULT, "intent": "unknown", "confidence": 0.0}

        message_clean = message.strip()[:500]  # Limiter la taille

        if self._client:
            result = self._analyze_with_openai(message_clean)
            if result:
                logger.info(
                    f"IA (OpenAI) : intent={result['intent']}, "
                    f"client='{result['client_name']}', "
                    f"article='{result['article_name']}', "
                    f"confidence={result['confidence']}"
                )
                return result

        # Fallback regex
        result = self._analyze_with_regex(message_clean)
        logger.info(
            f"IA (regex fallback) : intent={result['intent']}, "
            f"client='{result['client_name']}', "
            f"article='{result['article_name']}'"
        )
        return result

    # ── OpenAI ────────────────────────────────────────────────────────────────

    def _analyze_with_openai(self, message: str) -> Optional[dict]:
        """Analyse via OpenAI GPT. Retourne None en cas d'erreur."""
        try:
            response = self._client.chat.completions.create(
                model=self._model,
                messages=[
                    {"role": "system", "content": self.SYSTEM_PROMPT},
                    {"role": "user", "content": message},
                ],
                max_tokens=self._max_tokens,
                temperature=self._temperature,
                response_format={"type": "json_object"},
            )

            raw = response.choices[0].message.content.strip()
            data = json.loads(raw)
            return self._validate_and_normalize(data)

        except json.JSONDecodeError as e:
            logger.warning(f"OpenAI JSON invalide : {e}")
            return None
        except Exception as e:
            logger.error(f"Erreur OpenAI : {e}")
            return None

    # ── Regex Fallback ─────────────────────────────────────────────────────────

    # Patterns d'intentions
    _TARIF_PATTERNS = [
        r"\btarif\b", r"\bprix\b", r"\bcatalogue\b",
        r"\bliste.*(article|produit|prix)", r"\bpricelist\b",
        r"\bcombien.*(co[uû]te|vaut|price)",
    ]
    _STOCK_PATTERNS = [
        r"\bstock\b", r"\bdisponib", r"\bquantit", r"\binventaire\b",
        r"\bqté\b", r"\bdispo\b", r"\bencours\b", r"\bprovision\b",
        r"\b(?:recherche|cherche|trouve|liste)\b.*(?:article|produit|réf|ref|référence)",
        r"^\s*(?:recherche|cherche|trouve)\s+(?!(?:les?\s+)?(?:factures?|commandes?|devis|documents?|fournisseurs?|clients?|contacts?)\b).{2,80}$",
    ]
    _DOCUMENTS_PATTERNS = [
        r"\bfacture", r"\bdevis\b", r"\bcommande", r"\bdocument",
        r"\bbon.*(commande|livraison)", r"\bbl\b", r"\bbdc\b",
        r"\bhistorique\b", r"\brelev[eé]\b", r"\bcompte\b",
    ]
    _SUPPLIER_PATTERNS = [
        r"\bfournisseur", r"\bfourniseur", r"\bvendor\b",
    ]
    _ACTIONS_PATTERNS = [
        r"\baction", r"\bopportunit", r"\brelance", r"\bpipeline\b",
        r"\bprospect", r"\bcrm\b", r"\btâche\b", r"\btask\b",
    ]
    _HELP_PATTERNS = [
        r"\baide\b", r"\bhelp\b", r"\bcommande[s]?\b.*bot",
        r"\bque.*faire\b", r"\bcomment.*utiliser", r"^\s*/help\s*$",
    ]

    # Patterns extraction client et article
    _CLIENT_PATTERNS = [
        r"(?:client|compte|société|entreprise|partenaire|contact)\s*[:,\-]?\s*(?:de\s+|du\s+)?([A-Z][A-Za-zÀ-ÿ\s&\-\.]{2,40})",
        r"(?:factures?|commandes?|devis|documents?|historique)\s+(?:de\s+|du\s+|pour\s+)?(?:le\s+|la\s+|les\s+)?(?:contact\s+)?([A-Z][A-Za-zÀ-ÿ\s&\-\.]{2,40})",
        r"(?:pour|de|du)\s+([A-Z][A-Za-zÀ-ÿ\s&\-\.]{2,40})\s+(?:sas|sarl|sa|eurl|sci)?",
    ]
    _SUPPLIER_NAME_PATTERNS = [
        r"(?:fournisseurs?|fourniseurs?|vendor)\s*[:,\-]?\s*(?:de\s+|du\s+)?([A-Z][\w\s&\-\.]{2,60})",
        r"(?:factures?|commandes?|documents?)\s+(?:de\s+|du\s+|pour\s+)?(?:le\s+)?(?:fournisseur|fourniseur)\s*[:,\-]?\s*([A-Z][\w\s&\-\.]{2,60})",
    ]
    _STOCK_CATEGORY_PATTERNS = [
        r"stock\s+(?:de\s+|des\s+|pour\s+)?(?:la\s+|l['’]?|d['’]une\s+)?cat[eé]gorie\s+([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,60})",
        r"stock\s+(?:de\s+|des\s+|pour\s+)?(?:la\s+|l['’]?|d['’]une\s+)?famille\s+([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,60})",
    ]
    _STOCK_CATEGORY_PATTERNS.extend([
        r"stock\s+(?:de\s+|des\s+|du\s+|pour\s+)(?:l['’]|la\s+|le\s+|les\s+)(?!produit|article|r[eé]f[eé]rence|ref\b)([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,60})",
        r"stock\s+(?:en|sur)\s+([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,60})",
    ])
    _ARTICLE_PATTERNS = [
        r"(?:article|produit|référence|réf|ref)\s+([A-Za-zÀ-ÿ0-9\-_\.\/]{2,40})",
        r"stock\s+(?:du\s+|de\s+la\s+|de\s+)?([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,40})",
        r"(?:dispo|disponibilité)\s+(?:du\s+|de\s+)?([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,40})",
        r"^\s*(?:recherche|cherche|trouve)\s+(?!(?:les?\s+)?(?:factures?|commandes?|devis|documents?|fournisseurs?|clients?|contacts?)\b)([A-Za-zÀ-ÿ0-9\-_\.\/\s]{2,80})$",
    ]

    def _analyze_with_regex(self, message: str) -> dict:
        """Analyse par expressions régulières. Toujours disponible."""
        msg_lower = message.lower()
        result = {**DEFAULT_RESULT}

        # Détecter l'intention
        if any(re.search(p, msg_lower) for p in self._HELP_PATTERNS):
            result["intent"] = "help"
            result["confidence"] = 0.90
        elif any(re.search(p, msg_lower) for p in self._STOCK_PATTERNS):
            result["intent"] = "stock"
            result["confidence"] = 0.80
        elif any(re.search(p, msg_lower) for p in self._SUPPLIER_PATTERNS):
            result["intent"] = "supplier_documents"
            result["confidence"] = 0.85
        elif any(re.search(p, msg_lower) for p in self._DOCUMENTS_PATTERNS):
            result["intent"] = "client_documents"
            result["confidence"] = 0.80
        elif any(re.search(p, msg_lower) for p in self._TARIF_PATTERNS):
            result["intent"] = "tarif"
            result["confidence"] = 0.85
        elif any(re.search(p, msg_lower) for p in self._ACTIONS_PATTERNS):
            result["intent"] = "actions"
            result["confidence"] = 0.75
        else:
            result["intent"] = "unknown"
            result["confidence"] = 0.0

        # Extraire le nom du client
        for pattern in self._CLIENT_PATTERNS:
            match = re.search(pattern, message, re.IGNORECASE)
            if match:
                result["client_name"] = match.group(1).strip()
                break
        if result["intent"] == "supplier_documents":
            result["client_name"] = ""
            for pattern in self._SUPPLIER_NAME_PATTERNS:
                match = re.search(pattern, message, re.IGNORECASE)
                if match:
                    result["supplier_name"] = match.group(1).strip()
                    break
        if result["intent"] == "stock":
            result["client_name"] = ""

        # Extraire le filtre stock : categorie prioritaire, sinon article.
        if result["intent"] == "stock":
            for pattern in self._STOCK_CATEGORY_PATTERNS:
                match = re.search(pattern, message, re.IGNORECASE)
                if match:
                    result["article_name"] = f"categorie: {match.group(1).strip()}"
                    break

        if not result["article_name"]:
            for pattern in self._ARTICLE_PATTERNS:
                match = re.search(pattern, message, re.IGNORECASE)
                if match:
                    result["article_name"] = match.group(1).strip()
                    break

        return result

    # ── Validation ─────────────────────────────────────────────────────────────

    @staticmethod
    def _validate_and_normalize(data: dict) -> dict:
        """Valide et normalise le résultat OpenAI."""
        intent = str(data.get("intent", "unknown")).lower()
        if intent not in VALID_INTENTS:
            intent = "unknown"

        confidence = float(data.get("confidence", 0.0))
        confidence = max(0.0, min(1.0, confidence))

        return {
            "intent": intent,
            "client_name": str(data.get("client_name", "")).strip()[:100],
            "supplier_name": str(data.get("supplier_name", "")).strip()[:100],
            "article_name": str(data.get("article_name", "")).strip()[:100],
            "confidence": round(confidence, 2),
        }
