from datetime import timedelta
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIClient

from shop_management.models.data.account_transaction import AccountTransaction
from shop_management.models.data.account_transfer import AccountTransfer
from shop_management.models.data.shop import Shop
from shop_management.models.data.shop_account import Account
from shop_management.models.data.utils import Currency, ExchangeRate


class ControlledFinanceTransferTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="finance", password="test", is_staff=True)
        self.user.allowed_tabs = ["account_transfers", "accounts"]
        self.user.save(update_fields=["allowed_tabs"])
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.usd = Currency.objects.create(shop=self.shop, title="USD")
        self.afn = Currency.objects.create(shop=self.shop, title="AFN")
        self.sarafi = Account.objects.create(
            shop=self.shop,
            name="Ahmad Sarafi USD",
            currency=self.usd,
            account_type="SARAFI",
            sarafi_name="Ahmad",
            opening_balance="100",
        )
        self.cash = Account.objects.create(shop=self.shop, name="Kabul Cash AFN", currency=self.afn, account_type="CASH")
        self.fee_account = Account.objects.create(
            shop=self.shop,
            name="Fee Cash USD",
            currency=self.usd,
            account_type="CASH",
            opening_balance="2",
        )
        old_date = timezone.localdate() - timedelta(days=5)
        ExchangeRate.objects.create(shop=self.shop, from_currency=self.usd, to_currency=self.afn, rate="70", rate_date=old_date)
        ExchangeRate.objects.create(shop=self.shop, from_currency=self.usd, to_currency=self.afn, rate="75", rate_date=timezone.localdate() + timedelta(days=1))
        self.client = APIClient()
        self.client.force_authenticate(self.user)

    def create_draft(self):
        response = self.client.post("/api/shop/account-transfers/", {
            "from_account": self.sarafi.id, "to_account": self.cash.id,
            "amount": "100", "service_charge": "2", "service_charge_account": self.fee_account.id,
            "transfer_date": str(timezone.localdate()), "reference": "EX-1",
            "uses_controlled_engine": True,
        }, format="json")
        self.assertEqual(response.status_code, 201, response.data)
        return AccountTransfer.objects.get(pk=response.data["id"])

    def test_historical_rate_service_charge_and_void(self):
        transfer = self.create_draft()
        self.assertEqual(transfer.status, "DRAFT")
        self.assertEqual(transfer.exchange_rate, Decimal("70"))
        self.assertEqual(AccountTransaction.objects.count(), 0)
        response = self.client.post(f"/api/shop/account-transfers/{transfer.id}/post/")
        self.assertEqual(response.status_code, 200, response.data)
        transfer.refresh_from_db()
        self.assertEqual(transfer.converted_amount, Decimal("7000"))
        self.assertRegex(transfer.transfer_number, r"^KAB-FTR-\d{4}-\d{6}$")
        rows = list(AccountTransaction.objects.order_by("id"))
        self.assertEqual([(row.transaction_type, row.converted_amount) for row in rows], [("debit", Decimal("100")), ("credit", Decimal("7000")), ("debit", Decimal("2"))])
        response = self.client.post(f"/api/shop/account-transfers/{transfer.id}/void/", {"reason": "Rate correction"}, format="json")
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(AccountTransaction.objects.count(), 6)

    def test_posted_controlled_transfer_cannot_be_deleted(self):
        transfer = self.create_draft()
        self.client.post(f"/api/shop/account-transfers/{transfer.id}/post/")
        self.assertEqual(self.client.delete(f"/api/shop/account-transfers/{transfer.id}/").status_code, 400)

    def test_account_types_are_exposed(self):
        response = self.client.get("/api/shop/accounts/?page_size=all")
        self.assertEqual(response.status_code, 200)
        rows = response.data if isinstance(response.data, list) else response.data["results"]
        sarafi = next(row for row in rows if row["id"] == self.sarafi.id)
        self.assertEqual(sarafi["account_type"], "SARAFI")
        self.assertEqual(sarafi["sarafi_name"], "Ahmad")

    def test_internal_clearing_accounts_are_hidden_from_account_list(self):
        clearing = Account.objects.create(
            shop=self.shop,
            name="Sale USD Clearing",
            currency=self.usd,
            account_type="CASH",
            is_system_account=True,
        )

        response = self.client.get("/api/shop/accounts/?page_size=all")

        self.assertEqual(response.status_code, 200)
        rows = response.data if isinstance(response.data, list) else response.data["results"]
        self.assertNotIn(clearing.id, [row["id"] for row in rows])
