"""
DOOBAT - Tests unitaires OdooService
Copyright 2026 Vincent Gallet - Vincesoft
"""

import configparser
import sys
import os
import unittest
from unittest.mock import MagicMock, patch, call

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from services.odoo_service import OdooService, SimpleCache


# ── Fixtures ───────────────────────────────────────────────────────────────────

def make_config(overrides: dict = None) -> configparser.ConfigParser:
    config = configparser.ConfigParser()
    config["ODOO"] = {
        "url": "https://test.odoo.com",
        "db": "testdb",
        "username": "admin",
        "password": "secret",
        "timeout": "30",
        "max_retries": "3",
        "phone_fields": "phone,mobile",
    }
    if overrides:
        config["ODOO"].update(overrides)
    return config


PARTNER_COMMERCIAL = {
    "id": 1,
    "name": "Jean Dupont",
    "phone": "+33612345678",
    "mobile": "+33687654321",
    "email": "jean@test.com",
    "x_studio_titre_bot": "Commercial",
    "user_id": [10, "Jean Dupont"],
    "active": True,
}

PARTNER_CLIENT = {
    "id": 2,
    "name": "Client SA",
    "phone": "0123456789",
    "mobile": False,
    "email": "client@sa.fr",
    "x_studio_titre_bot": False,
    "user_id": [10, "Jean Dupont"],
    "active": True,
}


# ── Tests SimpleCache ──────────────────────────────────────────────────────────

class TestSimpleCache(unittest.TestCase):

    def test_set_and_get(self):
        cache = SimpleCache(ttl_seconds=60)
        cache.set("key1", {"data": 42})
        result = cache.get("key1")
        self.assertEqual(result, {"data": 42})

    def test_get_missing(self):
        cache = SimpleCache(ttl_seconds=60)
        self.assertIsNone(cache.get("nonexistent"))

    def test_expired(self):
        import time
        cache = SimpleCache(ttl_seconds=0)  # Expire immédiatement
        cache.set("key", "value")
        time.sleep(0.01)
        self.assertIsNone(cache.get("key"))

    def test_invalidate(self):
        cache = SimpleCache(ttl_seconds=60)
        cache.set("key", "value")
        cache.invalidate("key")
        self.assertIsNone(cache.get("key"))

    def test_clear(self):
        cache = SimpleCache(ttl_seconds=60)
        cache.set("k1", "v1")
        cache.set("k2", "v2")
        cache.clear()
        self.assertIsNone(cache.get("k1"))
        self.assertIsNone(cache.get("k2"))


# ── Tests clean_phone ──────────────────────────────────────────────────────────

class TestCleanPhone(unittest.TestCase):

    def test_format_local_to_international(self):
        result = OdooService.clean_phone("0612345678")
        self.assertEqual(result, "+33612345678")

    def test_already_international(self):
        result = OdooService.clean_phone("+33612345678")
        self.assertEqual(result, "+33612345678")

    def test_remove_spaces_dashes(self):
        result = OdooService.clean_phone("06 12-34.56 78")
        self.assertEqual(result, "+33612345678")

    def test_remove_parentheses(self):
        result = OdooService.clean_phone("(06) 12 34 56 78")
        self.assertEqual(result, "+33612345678")


# ── Tests is_commercial ────────────────────────────────────────────────────────

class TestIsCommercial(unittest.TestCase):

    def test_commercial_true(self):
        self.assertTrue(OdooService.is_commercial(PARTNER_COMMERCIAL))

    def test_commercial_false_client(self):
        self.assertFalse(OdooService.is_commercial(PARTNER_CLIENT))

    def test_commercial_false_empty(self):
        self.assertFalse(OdooService.is_commercial({"x_studio_titre_bot": ""}))

    def test_commercial_case_insensitive(self):
        self.assertTrue(OdooService.is_commercial({"x_studio_titre_bot": "commercial"}))
        self.assertTrue(OdooService.is_commercial({"x_studio_titre_bot": "COMMERCIAL"}))


# ── Tests OdooService avec mock ────────────────────────────────────────────────

class TestOdooService(unittest.TestCase):

    def setUp(self):
        self.config = make_config()
        with patch("xmlrpc.client.ServerProxy"):
            self.odoo = OdooService(self.config)
        self.odoo._uid = 1  # Simuler authentification
        self.odoo._partner_fields = {
            "id", "name", "phone", "mobile", "email",
            "x_studio_titre_bot", "user_id", "active",
        }

    def _mock_execute(self, return_value):
        """Helper pour mocker _execute."""
        self.odoo._execute = MagicMock(return_value=return_value)

    def test_find_partner_by_phone_found(self):
        self._mock_execute([PARTNER_COMMERCIAL])
        result = self.odoo.find_partner_by_phone("0612345678")
        self.assertIsNotNone(result)
        self.assertEqual(result["id"], 1)
        self.assertEqual(result["name"], "Jean Dupont")

    def test_find_partner_by_phone_not_found(self):
        self._mock_execute([])
        result = self.odoo.find_partner_by_phone("0000000000")
        self.assertIsNone(result)

    def test_find_partner_by_phone_uses_cache(self):
        self._mock_execute([PARTNER_COMMERCIAL])
        # Premier appel
        self.odoo.find_partner_by_phone("0612345678")
        # Deuxième appel (doit utiliser le cache)
        self.odoo.find_partner_by_phone("0612345678")
        # _execute ne doit être appelé qu'une fois
        self.assertEqual(self.odoo._execute.call_count, 1)

    def test_find_partner_by_id(self):
        self._mock_execute([PARTNER_CLIENT])
        result = self.odoo.find_partner_by_id(2)
        self.assertIsNotNone(result)
        self.assertEqual(result["name"], "Client SA")

    def test_find_partner_by_phone_uses_configured_fields(self):
        config = make_config({"phone_fields": "phone,mobile,x_studio_telephone_bot"})
        with patch("xmlrpc.client.ServerProxy"):
            odoo = OdooService(config)
        odoo._uid = 1
        odoo._partner_fields = {
            "id", "name", "phone", "mobile", "email",
            "x_studio_titre_bot", "x_studio_telephone_bot", "user_id", "active",
        }
        odoo._execute = MagicMock(return_value=[PARTNER_COMMERCIAL])

        odoo.find_partner_by_phone("+33612023942")

        _, _, domain = odoo._execute.call_args.args
        custom_field_variants = next(
            condition[2]
            for condition in domain
            if isinstance(condition, list)
            and condition[:2] == ["x_studio_telephone_bot", "in"]
        )
        self.assertIn("+33612023942", custom_field_variants)
        self.assertIn("0612023942", custom_field_variants)
        self.assertIn(["x_studio_telephone_bot", "ilike", "612023942"], domain)

    def test_check_client_assigned_true(self):
        self._mock_execute([PARTNER_CLIENT])
        result = self.odoo.check_client_assigned_to_commercial(2, 10)
        self.assertTrue(result)

    def test_check_client_assigned_false(self):
        self._mock_execute([])
        result = self.odoo.check_client_assigned_to_commercial(2, 99)
        self.assertFalse(result)

    def test_find_partner_by_phone_exception(self):
        self.odoo._execute = MagicMock(side_effect=Exception("Connexion échouée"))
        result = self.odoo.find_partner_by_phone("0612345678")
        self.assertIsNone(result)

    def test_get_all_products(self):
        mock_products = [
            {"id": 1, "name": "Produit A", "default_code": "REF001", "list_price": 10.0, "categ_id": [1, "Général"]},
        ]
        self._mock_execute(mock_products)
        result = self.odoo.get_all_products(limit=10)
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0]["name"], "Produit A")

    def test_get_full_supplier_documents(self):
        purchase_order = {"name": "PO001"}
        supplier_bill = {"name": "BILL001"}
        self.odoo.get_supplier_purchase_orders = MagicMock(return_value=[purchase_order])
        self.odoo.get_supplier_bills = MagicMock(return_value=[supplier_bill])

        result = self.odoo.get_full_supplier_documents(5, months=6)

        self.assertEqual(result["quotations"], [])
        self.assertEqual(result["orders"], [purchase_order])
        self.assertEqual(result["invoices"], [supplier_bill])


# ── Tests traductions statuts ──────────────────────────────────────────────────

class TestStatutTranslations(unittest.TestCase):

    def setUp(self):
        with patch("xmlrpc.client.ServerProxy"):
            self.odoo = OdooService(make_config())

    def test_order_states_fr(self):
        states = self.odoo.get_sale_order_states_fr()
        self.assertEqual(states["draft"], "Devis")
        self.assertEqual(states["sale"], "Commande confirmée")
        self.assertEqual(states["cancel"], "Annulée")

    def test_invoice_states_fr(self):
        states = self.odoo.get_invoice_states_fr()
        self.assertEqual(states["posted"], "Publiée")

    def test_payment_states_fr(self):
        states = self.odoo.get_payment_states_fr()
        self.assertEqual(states["paid"], "Payée")
        self.assertEqual(states["not_paid"], "Non payée")


if __name__ == "__main__":
    unittest.main(verbosity=2)
