# Archivo: CRMManagement.py
# Ruta: src\App\Utilities_module\CRMManagement.py
# Descripción: Módulo para gestionar la conexión con el CRM
# Autor: Equipo de Desarrollo IGSA
# Fecha: 2025

import requests
import time

class CRMManager:
    """
    Clase que gestiona la conexión y las operaciones con la API de
    Microsoft Dynamics 365 Business Central.
    """
    def __init__(self, config):
            """
            Inicializa el gestor del CRM con un objeto de configuración de Flask.
            
            Args:
                config: El objeto app.config (que se accede como diccionario).
            """
            # --- 🆕 CORRECCIÓN: Usar .get('CLAVE') en lugar de .ATRIBUTO ---
            self.tenant_id = config.get('BC_TENANT_ID')
            self.client_id = config.get('BC_CLIENT_ID')
            self.client_secret = config.get('BC_CLIENT_SECRET')
            self.env_name = config.get('BC_ENV_NAME')
            self.company_name = config.get('BC_COMPANY_NAME')

            # --- URLs de la API construidas con la configuración ---
            self.token_url = f"https://login.microsoftonline.com/{self.tenant_id}/oauth2/v2.0/token"
            self.api_base_url = f"https://api.businesscentral.dynamics.com/v2.0/{self.tenant_id}/{self.env_name}/api/v2.0"

            # --- Gestión interna del Token y GUID ---
            self.access_token = None
            self.token_expires_at = 0
            self.company_guid = None

    def _get_token(self):
        """
        Obtiene un token de acceso. Si el token existente es válido, lo reutiliza.
        De lo contrario, solicita uno nuevo.
        """
        if self.access_token and time.time() < self.token_expires_at - 60:
            return

        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        payload = {
            'grant_type': 'client_credentials',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'scope': 'https://api.businesscentral.dynamics.com/.default'
        }

        try:
            response = requests.post(self.token_url, headers=headers, data=payload)
            response.raise_for_status()
            data = response.json()
            
            self.access_token = data['access_token']
            self.token_expires_at = time.time() + data['expires_in']
            print("✅ Token de acceso obtenido/renovado exitosamente.")

        except requests.exceptions.RequestException as e:
            print(f"❌ Error al obtener el token: {e}")
            self.access_token = None
            raise

    def _get_company_guid(self):
        """
        Obtiene y almacena en caché el GUID de la compañía configurada.
        """
        if self.company_guid:
            return

        self._get_token()
        url = f"{self.api_base_url}/companies"
        headers = {'Authorization': f'Bearer {self.access_token}'}

        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
            companies = response.json().get('value', [])
            
            for company in companies:
                if company.get('name') == self.company_name:
                    self.company_guid = company.get('id')
                    print(f"✅ GUID para '{self.company_name}' encontrado: {self.company_guid}")
                    return
            
            raise Exception(f"No se encontró la compañía con el nombre '{self.company_name}'")

        except requests.exceptions.RequestException as e:
            print(f"❌ Error al obtener el GUID de la compañía: {e}")
            raise

    def _make_api_request(self, method, endpoint, payload=None):
        """
        Método genérico para realizar llamadas a los endpoints de la API.
        """
        self._get_token()
        self._get_company_guid()

        # Ajusta "IGSA/integration/v1.0" si la ruta de tu API es diferente.
        api_group_path = "IGSA/integration/v1.0"
        
        url = (f"https://api.businesscentral.dynamics.com/v2.0/{self.tenant_id}/{self.env_name}"
            f"/api/{api_group_path}/companies({self.company_guid})/{endpoint}")

        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        try:
            response = requests.request(method.upper(), url, headers=headers, json=payload)
            response.raise_for_status()
            return response.json() if response.content else {"status": "success", "code": response.status_code}

        except requests.exceptions.RequestException as e:
            print(f"❌ Error en la llamada API a '{endpoint}': {e}")
            print(f"Detalle del error: {e.response.text if e.response else 'Sin respuesta'}")
            raise

    # --- Métodos Públicos (Los que usará tu aplicación) ---

    def get_opportunities(self):
        """
        Obtiene la lista de todas las oportunidades del CRM.
        """
        print("ℹ️ Obteniendo lista de oportunidades...")
        return self._make_api_request('GET', 'opportunities')

    def send_quote(self, quote_data):
        """
        Envía los datos de una cotización al CRM para su importación.
        """
        print("ℹ️ Enviando cotización al CRM...")
        return self._make_api_request('POST', 'quoteImports', payload=quote_data)

    def get_opportunity_context(self, opportunity_no):
        """
        Obtiene y consolida el contexto completo (Oportunidad, Contacto y Vendedor) 
        para una cotización específica.
        """
        
        # 1. Obtener Oportunidad (Modo Seguro)
        opp_endpoint = f"opportunities?$filter=No eq '{opportunity_no}'"
        opportunities_data = self._make_api_request('GET', opp_endpoint)
        
        opportunity_list = opportunities_data.get('value', [])
        if not opportunity_list:
            raise ValueError(f"Oportunidad '{opportunity_no}' no encontrada en el CRM.")
        opportunity = opportunity_list[0]

        # 2. Obtener Contacto (Modo Seguro)
        contact_no = opportunity.get("ContactNo")
        contact = {}  # Iniciar como diccionario vacío
        
        if contact_no: # Solo buscar si la oportunidad tiene un número de contacto
            contact_endpoint = f"contactsImports?$filter=No eq '{contact_no}'"
            contacts_data = self._make_api_request('GET', contact_endpoint)
            contact_list = contacts_data.get('value', [])
            if contact_list:
                contact = contact_list[0]
            else:
                print(f"⚠️ Aviso: No se encontró la información detallada del Contacto No. {contact_no}.")
        else:
            print(f"⚠️ Aviso: La oportunidad {opportunity_no} no tiene un ContactNo asignado.")


        # 3. Obtener Vendedor (Modo Seguro)
        salesperson_code = opportunity.get("SalespersonCode")
        salesperson = {} # Iniciar como diccionario vacío
        
        if salesperson_code: # Solo buscar si la oportunidad tiene un vendedor
            sales_endpoint = f"salespersonImports?$filter=SalespersonCode eq '{salesperson_code}'"
            sales_data = self._make_api_request('GET', sales_endpoint)
            sales_list = sales_data.get('value', [])
            if sales_list:
                salesperson = sales_list[0]
            else:
                print(f"⚠️ Aviso: No se encontró la información del Vendedor Cód. {salesperson_code}.")
        else:
             print(f"⚠️ Aviso: La oportunidad {opportunity_no} no tiene un SalespersonCode asignado.")

        # 4. Consolidar la información
        # Esta sección ahora usa .get() para manejar campos que podrían no existir
        consolidated_context = {
            "No": opportunity.get("No"),
            "ContactName": contact.get("Name"),
            "ContactType": contact.get("Type"),
            "SalespersonName": salesperson.get("Name"),
            "ContactEMail": contact.get("EMail"),
            "Status": opportunity.get("Status"),
            "Address": contact.get("Address"),
            "Colony": contact.get("Colony"),
            "Phone": contact.get("Phone"),
            "City": contact.get("City"),
            "County": contact.get("County"),
            "Priority": opportunity.get("Priority"),
            "PostCode": contact.get("PostCode")
        }
        
        return consolidated_context