"""
Common Odoo XML-RPC client for seed scripts.
"""
import os
import xmlrpc.client
from typing import Any, Optional


class OdooClient:
    """XML-RPC client for Odoo operations."""

    def __init__(
        self,
        url: str = None,
        db: str = None,
        user: str = None,
        password: str = None,
    ):
        self.url = url or os.environ.get("ODOO_URL", "https://dop.optovia.ru")
        self.db = db or os.environ.get("ODOO_DB", "odoo")
        self.user = user or os.environ.get("ODOO_USER", "admin")
        self.password = password or os.environ.get("ODOO_PASSWORD", "admin")

        self._uid = None
        self._models = None
        self._country_cache = {}

    def connect(self) -> int:
        """Connect to Odoo and return user ID."""
        common = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/common")
        self._uid = common.authenticate(self.db, self.user, self.password, {})
        if not self._uid:
            raise Exception(f"Failed to authenticate with Odoo at {self.url}")
        self._models = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/object")
        print(f"Connected to Odoo at {self.url}, UID: {self._uid}")
        return self._uid

    @property
    def uid(self) -> int:
        if not self._uid:
            self.connect()
        return self._uid

    @property
    def models(self):
        if not self._models:
            self.connect()
        return self._models

    def execute(self, model: str, method: str, *args, **kwargs) -> Any:
        """Execute an Odoo model method."""
        return self.models.execute_kw(
            self.db, self.uid, self.password, model, method, args, kwargs
        )

    def search(self, model: str, domain: list, **kwargs) -> list:
        """Search for records."""
        return self.execute(model, "search", domain, **kwargs)

    def search_read(
        self, model: str, domain: list, fields: list = None, **kwargs
    ) -> list:
        """Search and read records."""
        opts = kwargs
        if fields:
            opts["fields"] = fields
        return self.execute(model, "search_read", domain, **opts)

    def read(self, model: str, ids: list, fields: list = None) -> list:
        """Read records by IDs."""
        return self.execute(model, "read", ids, {"fields": fields} if fields else {})

    def create(self, model: str, vals: dict) -> int:
        """Create a record."""
        return self.execute(model, "create", [vals])

    def write(self, model: str, ids: list, vals: dict) -> bool:
        """Update records."""
        return self.execute(model, "write", ids, vals)

    def unlink(self, model: str, ids: list) -> bool:
        """Delete records."""
        return self.execute(model, "unlink", ids)

    def get_country(self, code: str) -> Optional[int]:
        """Get country ID by ISO code (cached)."""
        if code in self._country_cache:
            return self._country_cache[code]

        result = self.search_read(
            "res.country", [["code", "=", code]], fields=["id"], limit=1
        )
        country_id = result[0]["id"] if result else None
        self._country_cache[code] = country_id
        return country_id

    def get_or_create(
        self, model: str, domain: list, vals: dict, name_field: str = "name"
    ) -> tuple[int, bool]:
        """
        Get existing record or create new one.
        Returns (id, created) tuple.
        """
        existing = self.search_read(model, domain, fields=["id"], limit=1)
        if existing:
            return existing[0]["id"], False

        record_id = self.create(model, vals)
        return record_id, True

    def get_reference(self, model: str, code_field: str, code: str) -> Optional[int]:
        """Get record ID by code field."""
        result = self.search_read(
            model, [[code_field, "=", code]], fields=["id"], limit=1
        )
        return result[0]["id"] if result else None
