from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.db import IntegrityError
from django.test import TestCase
from rest_framework.test import APIClient

from shop_management.models.data.organization import CompanySetting, Warehouse
from shop_management.models.data.shop import Shop
from shop_management.models.data.utils import Currency


class OrganizationModelTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username="owner", password="test-password"
        )
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.currency = Currency.objects.create(title="AFN", shop=self.shop)

    def test_company_setting_is_singleton(self):
        CompanySetting.objects.create(
            name=" Rahhed LTD .", base_currency=self.currency
        )
        with self.assertRaises(ValidationError):
            CompanySetting.objects.create(
                name="Second Company", base_currency=self.currency
            )

    def test_warehouse_code_is_normalized_and_unique_per_shop(self):
        warehouse = Warehouse.objects.create(
            shop=self.shop, code=" main ", name="Main Warehouse"
        )
        self.assertEqual(warehouse.code, "MAIN")
        with self.assertRaises(IntegrityError):
            Warehouse.objects.create(
                shop=self.shop, code="MAIN", name="Duplicate Warehouse"
            )


class OrganizationApiTests(TestCase):
    def setUp(self):
        self.owner = get_user_model().objects.create_user(
            username="owner-api", password="test-password", is_staff=True
        )
        self.owner.allowed_tabs = ["warehouses", "company_settings"]
        self.owner.save(update_fields=["allowed_tabs"])
        self.other_user = get_user_model().objects.create_user(
            username="other-api", password="test-password"
        )
        self.superuser = get_user_model().objects.create_superuser(
            username="admin-api", password="test-password", email="admin@example.com"
        )
        self.shop = Shop.objects.create(owner=self.owner, name="Kabul")
        self.other_shop = Shop.objects.create(owner=self.other_user, name="Takhar")
        self.currency = Currency.objects.create(title="AFN", shop=self.shop)
        self.client = APIClient()

    def test_warehouse_list_is_scoped_to_visible_shops(self):
        Warehouse.objects.create(shop=self.shop, code="MAIN", name="Main")
        Warehouse.objects.create(shop=self.other_shop, code="MAIN", name="Main")
        self.client.force_authenticate(self.owner)

        response = self.client.get("/api/shop/warehouses/?page_size=all")

        self.assertEqual(response.status_code, 200)
        results = response.data.get("results", response.data)
        self.assertEqual(len(results), 1)
        self.assertEqual(results[0]["shop"], self.shop.id)

    def test_branch_owner_can_create_only_accessible_branch_setting(self):
        payload = {
            "name": " Rahhed LTD .",
            "shop": self.shop.id,
            "base_currency": self.currency.id,
            "expiry_warning_days": 60,
        }
        self.client.force_authenticate(self.owner)
        created = self.client.post("/api/shop/company-settings/", payload)
        self.assertEqual(created.status_code, 201)
        self.assertEqual(CompanySetting.objects.get().shop_id, self.shop.id)

        other_currency = Currency.objects.create(title="AFN", shop=self.other_shop)
        payload.update(shop=self.other_shop.id, base_currency=other_currency.id)
        denied = self.client.post("/api/shop/company-settings/", payload)
        self.assertEqual(denied.status_code, 403)

    def test_invoice_settings_require_superadmin_and_are_in_branch_profile(self):
        from shop_management.serializers.data.organization import branch_profile_details
        setting = CompanySetting.objects.create(shop=self.shop, name="Kabul", base_currency=self.currency)
        url = f"/api/shop/company-settings/{setting.pk}/"
        payload = {"invoice_template": "jawid_tasal", "invoice_language": "ps", "invoice_direction": "rtl"}
        self.client.force_authenticate(self.owner)
        self.assertEqual(self.client.patch(url, payload).status_code, 400)
        self.client.force_authenticate(self.superuser)
        self.assertEqual(self.client.patch(url, payload).status_code, 200)
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        for field, value in payload.items():
            self.assertEqual(profile[field], value)
        self.assertEqual(self.client.patch(url, {"invoice_template": "unknown"}).status_code, 400)

    def test_letterhead_details_are_saved_per_branch_and_exposed_on_profile(self):
        from shop_management.serializers.data.organization import branch_profile_details
        self.client.force_authenticate(self.superuser)
        payload = {
            "shop": self.shop.pk, "name": "Mazar Branch", "legal_name": "Twaid Tasal LTD",
            "base_currency": self.currency.pk, "local_name": "شرکت تسل",
            "agency_name": "Mazar Agency", "invoice_tagline": "Branch products",
            "contact_person": "Branch manager", "facebook": "Tasal Mazar",
            "invoice_footer_note": "Thank you", "email": "mazar@example.com",
        }
        response = self.client.post("/api/shop/company-settings/", payload)
        self.assertEqual(response.status_code, 201, response.data)
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        for key in ("name", "legal_name", "local_name", "agency_name", "invoice_tagline", "contact_person", "facebook", "invoice_footer_note", "email"):
            self.assertEqual(profile[key], payload[key])
        self.assertEqual(branch_profile_details(self.other_shop), {"shop": self.other_shop.pk, "name": self.other_shop.name})

    def test_pashto_profile_survives_multipart_save_and_json_reload(self):
        from shop_management.serializers.data.organization import branch_profile_details

        self.client.force_authenticate(self.superuser)
        text = {
            "local_name": "جاوید تسل تجارتی شرکت",
            "agency_name": "د چرګانو څانګه",
            "invoice_tagline": "د نړۍ د معتبرو کمپنیو څخه حیواني درمل او واکسین",
            "contact_person": "جاوید (تسل) / ډاکټر هدایت الله",
            "facebook": "جاوید تسل تجارتی شرکت",
            "invoice_footer_note": "ستاسو له پېرود څخه مننه",
            "address": "جلال آباد ښار، نور عمر پلازه 5-6-7 نمبر دفتر",
        }
        response = self.client.post("/api/shop/company-settings/", {
            "shop": self.shop.pk, "name": "Poultry",
            "base_currency": self.currency.pk, **text,
        }, format="multipart")
        self.assertEqual(response.status_code, 201, response.data)
        url = f"/api/shop/company-settings/{response.data['id']}/"
        text["invoice_footer_note"] = "مننه، بیا راشئ"
        response = self.client.patch(url, text, format="multipart")
        self.assertEqual(response.status_code, 200, response.data)
        loaded = self.client.get(url).json()
        setting = CompanySetting.objects.get(pk=response.data["id"])
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        for field, expected in text.items():
            with self.subTest(field=field):
                self.assertEqual(getattr(setting, field), expected)
                self.assertEqual(loaded[field], expected)
                self.assertEqual(profile[field], expected)

    def test_custom_pashto_round_trip_with_ascii_json_and_image_upload(self):
        import io
        import json
        from PIL import Image
        from django.core.files.uploadedfile import SimpleUploadedFile
        from django.test import override_settings
        from tempfile import TemporaryDirectory
        self.client.force_authenticate(self.superuser)
        text = {
            "name": "زما نوې څانګه", "legal_name": "زما خپل شرکت",
            "local_name": "پښتو ژبه ښکلې ده", "agency_name": "ځانګړې نمایندګي",
            "invoice_tagline": "دا زما نوی متن دی، ږ ړ ښ څ ځ ټ ډ ڼ ئ ې ی 😊",
            "contact_person": "احمد او محمود", "facebook": "زما پاڼه",
            "invoice_footer_note": "بیا راشئ، ډېره مننه", "address": "کابل، نوی دفتر ۱۲۳",
        }
        payload = {"shop": self.shop.pk, "base_currency": self.currency.pk, **text}
        encoded = json.dumps(payload, ensure_ascii=True)
        self.assertTrue(encoded.isascii())
        response = self.client.generic("POST", "/api/shop/company-settings/",
            encoded.encode("ascii"), content_type="application/json; charset=utf-8")
        self.assertEqual(response.status_code, 201, response.data)
        url = f"/api/shop/company-settings/{response.data['id']}/"
        stream = io.BytesIO()
        Image.new("RGB", (12, 12), "white").save(stream, format="PNG")
        text["invoice_tagline"] = "له انځور سره نوی پښتو متن 😊"
        with TemporaryDirectory() as directory, override_settings(MEDIA_ROOT=directory):
            response = self.client.patch(url, {
                "profile_data": json.dumps(text, ensure_ascii=True),
                "logo": SimpleUploadedFile("pashto.png", stream.getvalue(), content_type="image/png"),
            }, format="multipart")
            self.assertEqual(response.status_code, 200, response.data)
            self.assertTrue(response.data["logo"])
            loaded = self.client.get(url).json()
            setting = CompanySetting.objects.get(pk=response.data["id"])
            for field, expected in text.items():
                self.assertEqual(response.data[field], expected)
                self.assertEqual(loaded[field], expected)
                self.assertEqual(getattr(setting, field), expected)
        for invalid in ("broken", "[]", "null"):
            response = self.client.patch(url, {"profile_data": invalid}, format="multipart")
            self.assertEqual(response.status_code, 400, response.data)

    def test_stale_damaged_profile_text_preserves_saved_pashto(self):
        setting = CompanySetting.objects.create(
            shop=self.shop, name="Poultry", base_currency=self.currency,
            local_name="جاوید تسل تجارتی شرکت",
        )
        self.client.force_authenticate(self.superuser)
        response = self.client.patch(f"/api/shop/company-settings/{setting.pk}/", {
            "local_name": "????? ??? ?????? ????",
        }, format="multipart")
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(response.data["local_name"], "جاوید تسل تجارتی شرکت")
        setting.refresh_from_db()
        self.assertEqual(setting.local_name, "جاوید تسل تجارتی شرکت")

    def test_poultry_save_restores_lost_text_and_preserves_readable_fields(self):
        from shop_management.branch_text import POULTRY_TEXT
        damaged = {field: "???? ??? ?????" for field in POULTRY_TEXT}
        setting = CompanySetting.objects.create(
            shop=self.shop, name="Poultry", base_currency=self.currency,
            invoice_template="jawid_poultry", **damaged,
        )
        self.client.force_authenticate(self.superuser)
        url = f"/api/shop/company-settings/{setting.pk}/"
        response = self.client.patch(url, {**damaged, "phone": "0700123456"}, format="multipart")
        self.assertEqual(response.status_code, 200, response.data)
        loaded = self.client.get(url).json()
        for field, expected in POULTRY_TEXT.items():
            self.assertEqual(loaded[field], expected)
        self.assertEqual(loaded["phone"], "0700123456")

    def test_unknown_damaged_custom_text_requires_correction(self):
        setting = CompanySetting.objects.create(
            shop=self.shop, name="Poultry", base_currency=self.currency,
            invoice_template="jawid_poultry",
        )
        self.client.force_authenticate(self.superuser)
        response = self.client.patch(f"/api/shop/company-settings/{setting.pk}/", {
            "invoice_tagline": "Custom text ????",
        }, format="multipart")
        self.assertEqual(response.status_code, 400)
        self.assertIn("invoice_tagline", response.data)

    def test_poultry_repair_preserves_custom_and_other_branch_text(self):
        from importlib import import_module
        from django.apps import apps
        from django.db import connection
        migration = import_module("shop_management.migrations.0119_repair_poultry_pashto")
        original = migration.TEXT["local_name"]
        masked = "".join("?" if ord(char) > 127 else char for char in original)
        setting = CompanySetting.objects.create(
            shop=self.shop, name="Poultry", base_currency=self.currency,
            invoice_template="jawid_poultry", local_name=masked,
            contact_person="Custom contact", address="Custom address",
        )
        from types import SimpleNamespace
        editor = SimpleNamespace(connection=connection)
        migration.repair_poultry_text(apps, editor)
        migration.repair_poultry_text(apps, editor)
        setting.refresh_from_db()
        self.assertEqual(setting.local_name, original)
        self.assertEqual(setting.contact_person, "Custom contact")
        self.assertEqual(setting.address, "Custom address")

    def test_branch_invoice_qr_upload_and_removal(self):
        import io
        from PIL import Image
        from django.core.files.uploadedfile import SimpleUploadedFile
        from django.test import override_settings
        from tempfile import TemporaryDirectory
        from shop_management.serializers.data.organization import branch_profile_details
        setting = CompanySetting.objects.create(shop=self.shop, name="Mazar", base_currency=self.currency)
        self.client.force_authenticate(self.superuser)
        stream = io.BytesIO()
        Image.new("RGB", (12, 12), "white").save(stream, format="PNG")
        with TemporaryDirectory() as directory, override_settings(MEDIA_ROOT=directory):
            upload = SimpleUploadedFile("qr.png", stream.getvalue(), content_type="image/png")
            url = f"/api/shop/company-settings/{setting.pk}/"
            response = self.client.patch(url, {"invoice_qr_code": upload}, format="multipart")
            self.assertEqual(response.status_code, 200, response.data)
            self.shop.refresh_from_db()
            self.assertIn("branch_invoice_qr/qr", branch_profile_details(self.shop)["invoice_qr_code"])
            response = self.client.patch(url, {"invoice_qr_code": ""}, format="multipart")
            self.assertEqual(response.status_code, 200, response.data)
            setting.refresh_from_db()
            self.assertFalse(setting.invoice_qr_code)

    def test_mazar_footer_generates_qr_without_uploaded_image(self):
        import base64
        import io
        from PIL import Image
        from shop_management.serializers.data.organization import branch_profile_details
        setting = CompanySetting.objects.create(
            shop=self.shop, name="Mazar", base_currency=self.currency,
            invoice_template="jawid_tasal", email="branch@example.com",
        )
        first = branch_profile_details(self.shop)["invoice_qr_code"]
        self.assertTrue(first.startswith("data:image/png;base64,"))
        image = Image.open(io.BytesIO(base64.b64decode(first.split(",", 1)[1])))
        self.assertEqual(image.format, "PNG")
        self.assertEqual(image.width, image.height)
        setting.email = "changed@example.com"
        setting.save()
        self.shop.refresh_from_db()
        self.assertNotEqual(first, branch_profile_details(self.shop)["invoice_qr_code"])
        self.assertFalse(setting.invoice_qr_code)

    def test_ikhlas_template_assignment_requires_superadmin(self):
        from shop_management.serializers.data.organization import branch_profile_details, contact_qr_data_uri
        setting = CompanySetting.objects.create(shop=self.shop, name="Ikhlas", base_currency=self.currency)
        url = f"/api/shop/company-settings/{setting.pk}/"
        self.client.force_authenticate(self.owner)
        self.assertEqual(self.client.patch(url, {"invoice_template": "ikhlas"}).status_code, 400)
        self.client.force_authenticate(self.superuser)
        response = self.client.patch(url, {"invoice_template": "ikhlas"})
        self.assertEqual(response.status_code, 200, response.data)
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        self.assertEqual(profile["invoice_template"], "ikhlas")
        self.assertEqual(profile["invoice_qr_code"], contact_qr_data_uri("farid.dilawar@outlook.com"))

    def test_poultry_template_assignment_and_dynamic_branch_details(self):
        from shop_management.serializers.data.organization import branch_profile_details, contact_qr_data_uri
        setting = CompanySetting.objects.create(shop=self.shop, name="Poultry", base_currency=self.currency)
        url = f"/api/shop/company-settings/{setting.pk}/"
        self.client.force_authenticate(self.owner)
        self.assertEqual(self.client.patch(url, {"invoice_template": "jawid_poultry"}).status_code, 400)
        self.client.force_authenticate(self.superuser)
        response = self.client.patch(url, {
            "invoice_template": "jawid_poultry", "agency_name": "Poultry Section",
            "phone": "0700123456", "email": "poultry@example.com",
        })
        self.assertEqual(response.status_code, 200, response.data)
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        self.assertEqual(profile["invoice_template"], "jawid_poultry")
        self.assertEqual(profile["agency_name"], "Poultry Section")
        self.assertEqual(profile["phone"], "0700123456")
        self.assertEqual(profile["invoice_qr_code"], contact_qr_data_uri("poultry@example.com"))

    def test_agriculture_template_assignment_and_branch_profile(self):
        from shop_management.serializers.data.organization import branch_profile_details, contact_qr_data_uri
        setting = CompanySetting.objects.create(shop=self.shop, name="Agriculture", base_currency=self.currency)
        url = f"/api/shop/company-settings/{setting.pk}/"
        self.client.force_authenticate(self.owner)
        self.assertEqual(self.client.patch(url, {"invoice_template": "jawid_agriculture"}).status_code, 400)
        self.client.force_authenticate(self.superuser)
        payload = {"invoice_template": "jawid_agriculture", "agency_name": "Agriculture Section", "phone": "0779958653 / 0748587408", "email": "agriculture@example.com"}
        response = self.client.patch(url, payload)
        self.assertEqual(response.status_code, 200, response.data)
        self.shop.refresh_from_db()
        profile = branch_profile_details(self.shop)
        for field, value in payload.items():
            self.assertEqual(profile[field], value)
        self.assertEqual(profile["invoice_qr_code"], contact_qr_data_uri(payload["email"]))

    def test_unused_warehouse_can_be_deleted(self):
        warehouse = Warehouse.objects.create(shop=self.shop, code="DELETE", name="Unused")
        self.client.force_authenticate(self.owner)
        response = self.client.delete(f"/api/shop/warehouses/{warehouse.pk}/")
        self.assertEqual(response.status_code, 204)
        self.assertFalse(Warehouse.objects.filter(pk=warehouse.pk).exists())

    def test_warehouse_delete_cannot_access_another_branch(self):
        warehouse = Warehouse.objects.create(shop=self.other_shop, code="KEEP", name="Other branch")
        self.client.force_authenticate(self.owner)
        response = self.client.delete(f"/api/shop/warehouses/{warehouse.pk}/")
        self.assertEqual(response.status_code, 404)
        self.assertTrue(Warehouse.objects.filter(pk=warehouse.pk).exists())

    def test_used_warehouse_returns_actionable_error_and_keeps_history(self):
        from shop_management.models.data.sale import Sale
        warehouse = Warehouse.objects.create(shop=self.shop, code="KEEP", name="Used warehouse")
        sale = Sale.objects.create(shop=self.shop, warehouse=warehouse, currency=self.currency)
        self.client.force_authenticate(self.owner)
        response = self.client.delete(f"/api/shop/warehouses/{warehouse.pk}/")
        self.assertEqual(response.status_code, 400, response.data)
        self.assertIn("Active warehouse", str(response.data["detail"]))
        self.assertTrue(Warehouse.objects.filter(pk=warehouse.pk).exists())
        self.assertTrue(Sale.objects.filter(pk=sale.pk,warehouse=warehouse).exists())
