"""Phase 5.3 — General Ledger & fiscal management models."""
from __future__ import annotations

import uuid
from datetime import date
from decimal import Decimal

from sqlalchemy import (
    Boolean,
    Date,
    ForeignKey,
    Index,
    Integer,
    Numeric,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column

from app.core.database import Base
from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin
from app.models.types import GUID


class GeneralLedger(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "general_ledgers"

    account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    journal_entry_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    entry_date: Mapped[date] = mapped_column(Date, nullable=False)
    debit: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    credit: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    running_balance: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    cost_center_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    project_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)

    __table_args__ = (
        Index("ix_gl_tenant_account_period", "tenant_id", "account_id", "fiscal_period_id"),
        Index("ix_gl_tenant_date", "tenant_id", "entry_date"),
    )


class LedgerBalance(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "ledger_balances"

    account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    opening_balance: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    debit_total: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    credit_total: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    closing_balance: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    current_balance: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "account_id", "fiscal_period_id",
            name="uq_ledger_balance_tenant_account_period",
        ),
    )


class TrialBalanceSnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "trial_balance_snapshots"

    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    generated_at: Mapped[date] = mapped_column(Date, nullable=False)
    total_debit: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    total_credit: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    difference: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    is_balanced: Mapped[bool] = mapped_column(Boolean, nullable=False)
    snapshot_data: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_trial_balance_tenant_period", "tenant_id", "fiscal_period_id"),
    )


class BalanceSnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "balance_snapshots"

    account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    snapshot_date: Mapped[date] = mapped_column(Date, nullable=False)
    balance: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    snapshot_type: Mapped[str] = mapped_column(String(50), nullable=False)

    __table_args__ = (
        Index("ix_balance_snapshot_tenant_account", "tenant_id", "account_id"),
    )


class ClosingEntry(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "closing_entries"

    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    entry_type: Mapped[str] = mapped_column(String(50), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_closing_entries_period", "tenant_id", "fiscal_period_id"),
    )


class OpeningEntry(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "opening_entries"

    fiscal_year_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    is_debit: Mapped[bool] = mapped_column(Boolean, nullable=False)

    __table_args__ = (
        Index("ix_opening_entries_year", "tenant_id", "fiscal_year_id"),
    )


class AccountingCalendar(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "accounting_calendars"

    name: Mapped[str] = mapped_column(String(100), nullable=False)
    fiscal_year_start_month: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    periods_per_year: Mapped[int] = mapped_column(Integer, default=12, nullable=False)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "name", name="uq_accounting_calendar_tenant_name"),
    )
