import uuid as _uuid

from odoo import models, fields, api, _
from odoo.exceptions import UserError
import requests
import logging

_logger = logging.getLogger(__name__)


class IapAccount(models.Model):
    _inherit = "iap.account"

    ocr_credits_balance = fields.Integer(
        string="OCR Real Balance",
        readonly=True,
        help="Credits available on your local OCR Extract Server",
    )

    def _get_ocr_db_name(self):
        """Get the OCR database name from config or default."""
        params_sudo = self.env["ir.config_parameter"].sudo()
        return params_sudo.get_param("iap_extract_database", "")

    def _ocr_jsonrpc(self, url, params):
        # Handle internal Docker communication
        params_sudo = self.env["ir.config_parameter"].sudo()
        internal_endpoint = params_sudo.get_param("iap_extract_endpoint_internal", "")
        use_db_header = False
        if internal_endpoint:
            public_endpoint = params_sudo.get_param("iap_extract_endpoint", "").rstrip("/")
            if public_endpoint and url.startswith(public_endpoint):
                url = url.replace(public_endpoint, internal_endpoint.rstrip("/"))
                use_db_header = True
                _logger.debug("Redirecting OCR call to internal endpoint: %s", url)

        payload = {
            "jsonrpc": "2.0",
            "method": "call",
            "params": params,
            "id": str(_uuid.uuid4()),
        }
        try:
            _logger.debug("OCR JSON-RPC call to %s with params keys: %s", url, list(params.keys())) # Trace les paramètres de l'appel JSON-RPC
            headers = {"Content-Type": "application/json"}
            if use_db_header:
                headers["X-Odoo-Database"] = self._get_ocr_db_name()
            response = requests.post(
                url,
                json=payload,
                timeout=30,
                headers=headers,
                verify=False,
            )
            response.raise_for_status()
            res_data = response.json()
            _logger.debug("OCR JSON-RPC response from %s: %s", url, res_data.get("result", {}).get("status", "no-status")) # Trace le statut de la réponse JSON-RPC
            if "error" in res_data:
                _logger.error("OCR JSON-RPC Error: %s", res_data["error"]) # Log l'erreur retournée par le serveur OCR
                err_msg = res_data["error"]
                if isinstance(err_msg, dict):
                    err_msg = err_msg.get("message", str(err_msg))
                return {"status": "error", "message": err_msg}
            return res_data.get("result", {})
        except Exception as e:
            _logger.error("OCR Connection Error to %s: %s", url, str(e)) # Log l'exception de connexion au serveur OCR
            return {"status": "error", "message": str(e)}

    def action_request_ocr_recharge(self):
        self.ensure_one()
        # Try to provision token if missing, but don't block if the server is unreachable
        if not self.account_token or self.account_token == "dummy_token":
            try:
                self.action_provision_ocr_token()
            except Exception as e:
                _logger.warning("Could not provision OCR token via JSON-RPC: %s. Fallback to web parameters.", str(e))

        params_sudo = self.env["ir.config_parameter"].sudo()
        dbuuid = params_sudo.get_param("database.uuid", "")
        token = (self.account_token or "").strip()

        # Redirect to the credit portal (separate Odoo instance for order processing)
        url = "https://erp.amcs.tn/ocr/packs"

        import urllib.parse

        params = {
            'dbuuid': dbuuid,
            'token': token if token != 'dummy_token' else '',
            'company_name': self.env.company.name,
            'email': self.env.user.email or '',
            'client_url': params_sudo.get_param("web.base.url"),
        }

        query_string = "&".join([f"{k}={urllib.parse.quote(v)}" for k, v in params.items() if v])
        if query_string:
            url += f"?{query_string}"

        return {
            "type": "ir.actions.act_url",
            "url": url,
            "target": "new",
        }

    def action_refresh_ocr_balance(self):
        self.ensure_one()
        if not self.account_token or self.account_token == "dummy_token":
            self.action_provision_ocr_token()
 
        params_sudo = self.env["ir.config_parameter"].sudo()
        endpoint = params_sudo.get_param("iap_extract_endpoint", "")
        if not endpoint:
            raise UserError(_("OCR endpoint is not configured. Please check System Parameters."))
        endpoint = endpoint.rstrip("/")
        url = f"{endpoint}/ocr/1/credit"
 
        dbuuid = params_sudo.get_param("database.uuid", "")
        res_data = self._ocr_jsonrpc(url, {
            "account_token": self.account_token.strip(),
            "dbuuid": dbuuid,
        })
 
        reprovision_messages = ["Invalid token", "Token and dbuuid do not match"]
        if isinstance(res_data, dict) and res_data.get("status") != "success" and res_data.get("message") in reprovision_messages:
            _logger.warning("Token invalid on remote OCR server. Reprovisioning...")
            self.action_provision_ocr_token()
            res_data = self._ocr_jsonrpc(url, {
                "account_token": self.account_token.strip(),
                "dbuuid": dbuuid,
            })
 
        while isinstance(res_data, list):
            res_data = res_data[0] if res_data else {}
 
        if isinstance(res_data, dict) and res_data.get("status") == "success":
            balance = res_data.get("credits", 0)
            with self.env.cr.savepoint():
                self.write({"ocr_credits_balance": int(balance)})
            _logger.info(
                "Checked OCR balance for token %s: %s", self.account_token[:8], balance
            )
        else:
            _logger.error("Failed to check OCR balance: %s", res_data.get("message"))


    def action_provision_ocr_token(self):
        self.ensure_one()
 
        params_sudo = self.env["ir.config_parameter"].sudo()
        endpoint = params_sudo.get_param("iap_extract_endpoint", "")
        if not endpoint:
            raise UserError(_("OCR endpoint is not configured. Please check System Parameters."))
        endpoint = endpoint.rstrip("/")
        url = f"{endpoint}/ocr/1/token/generate"
 
        dbuuid = params_sudo.get_param("database.uuid")
        if not dbuuid:
            raise UserError(_("Database UUID not found. Cannot provision token."))
 
        payload = {
            "dbuuid": dbuuid,
            "service_name": "invoice_ocr",
            "company_name": self.env.company.name,
            "client_url": params_sudo.get_param("web.base.url"),
        }
 
        res_data = self._ocr_jsonrpc(url, payload)
        _logger.info("OCR Provisioning Response: %s", res_data) # Log la réponse brute du générateur de token
 
        if res_data.get("status") == "success":
            token = res_data.get("token")
            self.write({"account_token": token})
            params_sudo.set_param("ocr_api_token", token)
            _logger.info("Successfully provisioned OCR token: %s", token[:8]) # Confirme la réussite de la provision du token
            return True
        else:
            _logger.error("Failed to provision OCR token: %s", res_data.get("message")) # Log l'échec de la demande de token
            raise UserError(
                _("Failed to contact OCR server: %s")
                % res_data.get("message", "Unknown Error")
            )
 

    @api.model
    def _cron_refresh_all_ocr_balances(self):
        accounts = self.search([('service_name', '=', 'invoice_ocr')])
        for account in accounts:
            try:
                account.action_refresh_ocr_balance()
            except Exception as e:
                _logger.error("Failed to refresh OCR balance for account %s: %s", account.id, e)

    @api.model
    def get_ocr_account(self):
        """Helper to get the OCR IAP account."""
        return self.search([('service_name', '=', 'invoice_ocr')], limit=1)

    @api.model
    def search_or_create_ocr_account(self):
        """
        Safely search for existing OCR account or create a new one.
        Called from post_init_hook to avoid installation failures.
        """
        service_name = "invoice_ocr"
        existing = self.search([("service_name", "=", service_name)], limit=1)
        if not existing:
            _logger.info("Creating initial IAP account for %s", service_name)
            vals = {
                "service_name": service_name,
                "account_token": "dummy_token",
            }

            # T021: Handle Odoo versions where service_id is mandatory on iap.account
            if "service_id" in self._fields:
                IapService = self.env["iap.service"]
                service = IapService.search([
                    "|",
                    ("name", "=", service_name),
                    ("technical_name", "=", service_name)
                ], limit=1)
                if not service:
                    service = IapService.create({
                        "name": service_name,
                        "technical_name": service_name,
                        "description": "OCR Service for Invoice Extraction",
                        "unit_name": "Credits",
                        "integer_balance": True,
                    })
                vals["service_id"] = service.id

            try:
                self.create(vals)
            except Exception as e:
                _logger.error("Failed to create IAP account: %s", e)
        return True
