from decimal import Decimal

from django.db import transaction

from shop_management.models.data.accounting import JournalEntry, JournalEntryLine, LedgerAccount
from shop_management.services.chart_of_accounts import initialize_standard_chart
from shop_management.services.journals import post_journal, void_journal


FINANCIAL_LEDGER_CODES = {"CASH": "1100", "BANK": "1200", "SARAFI": "1300", "OTHER": "1100"}


def financial_ledger_code(account):
    return FINANCIAL_LEDGER_CODES.get(getattr(account, "account_type", "OTHER"), "1100")


def expense_ledger_code(category):
    name = str(getattr(category, "name", "") or "").lower()
    if "salary" in name or "wage" in name: return "6100"
    if "rent" in name: return "6200"
    if "util" in name or "electric" in name or "water" in name: return "6300"
    if "transport" in name or "delivery" in name: return "6400"
    if "transfer" in name or "charge" in name or "fee" in name: return "6500"
    return "6900"


@transaction.atomic
def create_system_journal(*, shop, entry_date, source_type, source_id, description, currency, exchange_rate, lines, user):
    existing = JournalEntry.objects.filter(shop=shop, source_type=source_type, source_id=str(source_id)).first()
    if existing:
        return existing
    initialize_standard_chart(shop, user=user)
    accounts = {row.code: row for row in LedgerAccount.objects.filter(shop=shop)}
    entry = JournalEntry.objects.create(
        shop=shop, entry_date=entry_date, description=description,
        source_type=source_type, source_id=str(source_id), created_by=user,
    )
    for row in lines:
        amount = Decimal(str(row["amount"])).quantize(Decimal("0.01"))
        if amount <= 0:
            continue
        code = row["code"]
        if code not in accounts:
            raise ValueError(f"Standard ledger account {code} is missing.")
        side = row["side"]
        JournalEntryLine.objects.create(
            journal_entry=entry, ledger_account=accounts[code], transaction_currency=currency,
            debit=amount if side == "debit" else 0, credit=amount if side == "credit" else 0,
            exchange_rate_used=exchange_rate, description=row.get("description", ""),
            customer=row.get("customer"), supplier=row.get("supplier"),
            financial_account=row.get("financial_account"), created_by=user,
        )
    return post_journal(entry.pk, user=user)


def void_system_journal(*, shop, source_type, source_id, user, reason):
    entry = JournalEntry.objects.filter(shop=shop, source_type=source_type, source_id=str(source_id)).first()
    if entry and entry.status == JournalEntry.Status.POSTED:
        return void_journal(entry.pk, user=user, reason=reason)
    return entry
