"""
Bootstrap account types and default chart of accounts for new installs.

Called from API read endpoints (lazy bootstrap), setup endpoints, and migrations.
"""
from __future__ import annotations

from .accounting_defaults import (
    COA_NUMBER_RANGES,
    DEFAULT_CHART_OF_ACCOUNTS,
    DEFAULT_EXPENSE_CATEGORY_MAPPINGS,
    DEFAULT_POSTING_RULES,
)
from .models import (
    ACCOUNT_CATEGORY_MAP,
    AccountMapping,
    AccountType,
    AccountTypeDetail,
    ChartOfAccount,
)

DEFAULT_ACCOUNT_TYPES = [
    {
        "type_key": "cash_and_cash_equivalents",
        "name": "Cash and Cash Equivalents",
        "details": ["Cash on Hand", "Petty Cash", "Cash Equivalents"],
    },
    {
        "type_key": "bank",
        "name": "Bank",
        "details": ["Bank Account", "Savings Account", "Foreign Currency Bank Account"],
    },
    {
        "type_key": "accounts_receivable",
        "name": "Accounts Receivable",
        "details": ["Accounts Receivable", "Allowance for Bad Debts", "Trade Debtors"],
    },
    {
        "type_key": "current_assets",
        "name": "Current Assets",
        "details": [
            "Inventory",
            "Prepaid Expenses",
            "Short-Term Investments",
            "VAT Receivable",
            "Other Current Assets",
        ],
    },
    {
        "type_key": "fixed_assets",
        "name": "Fixed Assets",
        "details": [
            "Machinery & Equipment",
            "Furniture & Fixtures",
            "Vehicles",
            "Computer Equipment",
            "Leasehold Improvements",
            "Accumulated Depreciation",
        ],
    },
    {
        "type_key": "non_current_assets",
        "name": "Non-Current Assets",
        "details": ["Long-Term Investments", "Intangible Assets", "Goodwill", "Other Non-Current Assets"],
    },
    {
        "type_key": "accounts_payable",
        "name": "Accounts Payable",
        "details": ["Accounts Payable", "Trade Creditors"],
    },
    {
        "type_key": "credit_card",
        "name": "Credit Card",
        "details": ["Credit Card", "Corporate Credit Card"],
    },
    {
        "type_key": "current_liabilities",
        "name": "Current Liabilities",
        "details": [
            "Accrued Liabilities",
            "Wages Payable",
            "VAT Payable",
            "Income Tax Payable",
            "Unearned Revenue",
            "Short-Term Loans",
            "Customer Deposits",
            "Goods Received Not Invoiced",
            "Other Current Liabilities",
        ],
    },
    {
        "type_key": "non_current_liabilities",
        "name": "Non-Current Liabilities",
        "details": ["Long-Term Loans", "Deferred Tax Liabilities", "Other Non-Current Liabilities"],
    },
    {
        "type_key": "owners_equity",
        "name": "Owner's Equity",
        "details": [
            "Share Capital",
            "Retained Earnings",
            "Opening Balance Equity",
            "Dividends Paid",
            "Other Owner's Equity",
        ],
    },
    {
        "type_key": "income",
        "name": "Income",
        "details": [
            "Sales Revenue",
            "Service Revenue",
            "Consulting Income",
            "Discounts Given",
            "Other Primary Income",
        ],
    },
    {
        "type_key": "other_income",
        "name": "Other Income",
        "details": [
            "Interest Income",
            "Rental Income",
            "Vehicle & Equipment Lease Revenue",
            "Gain on Disposal of Assets",
            "Miscellaneous Income",
        ],
    },
    {
        "type_key": "cost_of_sales",
        "name": "Cost of Sales",
        "details": [
            "Cost of Goods Sold",
            "Direct Labour",
            "Direct Materials",
            "Freight and Delivery",
            "Other Cost of Sales",
        ],
    },
    {
        "type_key": "expenses",
        "name": "Expenses",
        "details": [
            "Salaries & Wages",
            "Rent Expense",
            "Utilities",
            "Office Supplies",
            "Insurance",
            "Marketing & Advertising",
            "Travel & Entertainment",
            "Telephone & Internet",
            "Bank Charges",
            "Depreciation Expense",
            "Repairs & Maintenance",
            "Legal & Professional Fees",
            "Tax & Licence Expense",
            "Other Expenses",
        ],
    },
    {
        "type_key": "other_expense",
        "name": "Other Expense",
        "details": ["Interest Expense", "Loss on Disposal of Assets", "Other Non-Operating Expense"],
    },
]


def seed_account_types() -> dict:
    """Create standard account types and detail sub-types. Idempotent."""
    created_count = 0
    for td in DEFAULT_ACCOUNT_TYPES:
        stmt, normal = ACCOUNT_CATEGORY_MAP[td["type_key"]]
        at, created = AccountType.objects.get_or_create(
            type_key=td["type_key"],
            defaults={"name": td["name"], "financial_statement": stmt, "normal_balance": normal},
        )
        if created:
            created_count += 1
        for detail_name in td["details"]:
            AccountTypeDetail.objects.get_or_create(account_type=at, name=detail_name)

    return {
        "created": created_count,
        "total_types": AccountType.objects.count(),
    }


def ensure_account_types() -> dict:
    """Seed account types when the table is empty."""
    if AccountType.objects.exists():
        return {"created": 0, "total_types": AccountType.objects.count(), "bootstrapped": False}
    result = seed_account_types()
    result["bootstrapped"] = True
    return result


def branch_has_default_coa(branch) -> bool:
    """True when at least one standard COA account number exists for the branch."""
    if not branch:
        return False
    default_numbers = [row[0] for row in DEFAULT_CHART_OF_ACCOUNTS]
    return ChartOfAccount.objects.filter(
        company_branch=branch,
        account_number__in=default_numbers,
        recycle_bin=False,
    ).exists()


def seed_default_chart_of_accounts(branch, staff) -> dict:
    """
    Seed enterprise COA (1000–7999) and standard posting rules for a branch.
    Requires account types. Idempotent (skips existing account numbers).
    """
    ensure_account_types()

    created_accounts = 0
    accounts_by_number = {}

    for number, name, type_key, detail_name in DEFAULT_CHART_OF_ACCOUNTS:
        existing = ChartOfAccount.objects.filter(
            company_branch=branch,
            account_number=number,
            recycle_bin=False,
        ).first()
        if existing:
            accounts_by_number[number] = existing
            continue

        account_type = AccountType.objects.filter(type_key=type_key).first()
        if not account_type:
            continue
        detail = AccountTypeDetail.objects.filter(
            account_type=account_type,
            name=detail_name,
        ).first()
        if not detail:
            detail, _ = AccountTypeDetail.objects.get_or_create(
                account_type=account_type,
                name=detail_name,
            )

        account = ChartOfAccount.objects.create(
            company_branch=branch,
            account_type=account_type,
            account_detail_type=detail,
            account_name=name,
            account_number=number,
            is_default_account=True,
            created_by=staff,
            last_updated_by=staff,
        )
        accounts_by_number[number] = account
        created_accounts += 1

    created_mappings = 0
    for rule in DEFAULT_POSTING_RULES:
        debit = accounts_by_number.get(rule["debit_number"])
        credit = accounts_by_number.get(rule["credit_number"])
        if not debit or not credit:
            debit = ChartOfAccount.objects.filter(
                company_branch=branch,
                account_number=rule["debit_number"],
                recycle_bin=False,
            ).first()
            credit = ChartOfAccount.objects.filter(
                company_branch=branch,
                account_number=rule["credit_number"],
                recycle_bin=False,
            ).first()
        if not debit or not credit:
            continue

        _, created = AccountMapping.objects.update_or_create(
            mapping_type=rule["mapping_type"],
            source_key=rule["source_key"],
            company_branch=branch,
            defaults={
                "source_label": rule["source_label"],
                "debit_account": debit,
                "credit_account": credit,
                "description": rule["description"],
                "created_by": staff,
                "last_updated_by": staff,
            },
        )
        if created:
            created_mappings += 1

    for rule in DEFAULT_EXPENSE_CATEGORY_MAPPINGS:
        debit = accounts_by_number.get(rule["debit_number"])
        if not debit:
            debit = ChartOfAccount.objects.filter(
                company_branch=branch,
                account_number=rule["debit_number"],
                recycle_bin=False,
            ).first()
        if not debit:
            continue
        _, created = AccountMapping.objects.update_or_create(
            mapping_type="expense_category",
            source_key=rule["source_key"],
            company_branch=branch,
            defaults={
                "source_label": rule["source_label"],
                "debit_account": debit,
                "credit_account": None,
                "description": rule["description"],
                "created_by": staff,
                "last_updated_by": staff,
            },
        )
        if created:
            created_mappings += 1

    return {
        "created_accounts": created_accounts,
        "created_mappings": created_mappings,
        "total_accounts": ChartOfAccount.objects.filter(
            company_branch=branch,
            recycle_bin=False,
        ).count(),
        "number_ranges": [
            {"range": r[0], "label": r[1], "statement": r[2]} for r in COA_NUMBER_RANGES
        ],
    }


def ensure_branch_default_coa(branch, staff) -> dict | None:
    """Seed default COA for a branch when none of the standard accounts exist."""
    if not branch or branch_has_default_coa(branch):
        return None
    result = seed_default_chart_of_accounts(branch, staff)
    result["bootstrapped"] = True
    return result
