from decimal import Decimal

from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.test import TestCase
from rest_framework.test import APIClient

from shop_management.models.data.account_transaction import AccountTransaction
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
from shop_management.services.account_controls import ensure_debit_allowed


class FinanceAuditControlTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="auditor", password="test")
        self.user.is_staff = True
        self.user.is_superuser = True
        self.user.save(update_fields=["is_staff", "is_superuser"])
        self.client = APIClient()
        self.client.force_authenticate(self.user)
        self.shop = Shop.objects.create(owner=self.user, name="Audit Branch")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        self.account = Account.objects.create(
            shop=self.shop, currency=self.currency, name="Cash", opening_balance=Decimal("100")
        )

    def test_unapproved_negative_balance_is_blocked(self):
        with self.assertRaises(ValidationError):
            ensure_debit_allowed(self.account, Decimal("101"), label="payment")

    def test_intentional_overdraft_can_be_enabled(self):
        self.account.allow_overdraft = True
        self.account.save(update_fields=["allow_overdraft"])
        _, projected = ensure_debit_allowed(self.account, Decimal("150"), label="payment")
        self.assertEqual(projected, Decimal("-50"))

    def test_financial_movement_cannot_be_deleted(self):
        movement = AccountTransaction.objects.create(
            account=self.account,
            account_currency=self.currency,
            transaction_currency=self.currency,
            transaction_type="credit",
            amount=Decimal("10"),
            exchange_rate=Decimal("1"),
            converted_amount=Decimal("10"),
            description="Opening test movement",
        )
        with self.assertRaises(ValidationError):
            movement.delete()

    def test_manual_movement_is_attributed_and_reversed_with_reason(self):
        response = self.client.post("/api/shop/account-transactions/", {
            "account": self.account.pk,
            "transaction_currency": self.currency.pk,
            "transaction_type": "credit",
            "amount": "25.00",
            "description": "Cash count correction",
            "transaction_date": "2026-09-20",
        }, format="json")
        self.assertEqual(response.status_code, 201, response.data)
        movement = AccountTransaction.objects.get(pk=response.data["id"])
        self.assertEqual(movement.created_by, self.user)
        self.assertEqual(movement.source_type, "MANUAL")

        reversed_response = self.client.post(
            f"/api/shop/account-transactions/{movement.pk}/reverse/",
            {"reason": "Cash count was entered twice"},
            format="json",
        )
        self.assertEqual(reversed_response.status_code, 201, reversed_response.data)
        movement.refresh_from_db()
        self.assertIsNotNone(movement.reversed_at)
        self.assertEqual(movement.reversal_entries.count(), 1)

    def test_audit_endpoint_reports_negative_accounts(self):
        self.account.opening_balance = Decimal("-10")
        self.account.save(update_fields=["opening_balance"])
        response = self.client.get("/api/shop/account-transactions/audit/")
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(response.data["summary"]["negative_accounts"], 1)
