"""Phase 5.4 — Treasury management domain 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, relationship

from app.core.database import Base
from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin
from app.models.types import (
    ChequeStatus,
    GUID,
    ReconciliationStatus,
    TreasuryTransactionType,
)


class TreasurySettings(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "treasury_settings"

    key: Mapped[str] = mapped_column(String(100), nullable=False)
    value: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "key", name="uq_treasury_settings_tenant_key"),
    )


class CashBox(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "cash_boxes"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    currency_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    opening_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
    )
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    transactions: Mapped[list["CashTransaction"]] = relationship(back_populates="cash_box")

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_cash_box_tenant_code"),
    )


class CashTransaction(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "cash_transactions"

    cash_box_id: Mapped[uuid.UUID] = mapped_column(
        GUID(), ForeignKey("cash_boxes.id", ondelete="RESTRICT"), nullable=False
    )
    transaction_type: Mapped[TreasuryTransactionType] = mapped_column(nullable=False)
    transaction_date: Mapped[date] = mapped_column(Date, nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    reference_number: Mapped[str | None] = mapped_column(String(100), nullable=True)

    cash_box: Mapped["CashBox"] = relationship(back_populates="transactions")

    __table_args__ = (
        Index("ix_cash_tx_tenant_box", "tenant_id", "cash_box_id"),
    )


class Bank(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "banks"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    swift_code: Mapped[str | None] = mapped_column(String(20), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    accounts: Mapped[list["BankAccount"]] = relationship(back_populates="bank")

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_bank_tenant_code"),
    )


class BankAccount(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "bank_accounts"

    bank_id: Mapped[uuid.UUID] = mapped_column(
        GUID(), ForeignKey("banks.id", ondelete="RESTRICT"), nullable=False
    )
    account_number: Mapped[str] = mapped_column(String(50), nullable=False)
    iban: Mapped[str | None] = mapped_column(String(34), nullable=True)
    card_number: Mapped[str | None] = mapped_column(String(20), nullable=True)
    account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    currency_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    opening_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
    )
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    bank: Mapped["Bank"] = relationship(back_populates="accounts")
    transactions: Mapped[list["BankTransaction"]] = relationship(back_populates="bank_account")

    __table_args__ = (
        UniqueConstraint("tenant_id", "account_number", name="uq_bank_account_tenant_number"),
    )


class BankTransaction(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "bank_transactions"

    bank_account_id: Mapped[uuid.UUID] = mapped_column(
        GUID(), ForeignKey("bank_accounts.id", ondelete="RESTRICT"), nullable=False
    )
    transaction_type: Mapped[TreasuryTransactionType] = mapped_column(nullable=False)
    transaction_date: Mapped[date] = mapped_column(Date, nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    reference_number: Mapped[str | None] = mapped_column(String(100), nullable=True)

    bank_account: Mapped["BankAccount"] = relationship(back_populates="transactions")

    __table_args__ = (
        Index("ix_bank_tx_tenant_account", "tenant_id", "bank_account_id"),
    )


class ChequeBook(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "cheque_books"

    bank_account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    serial_from: Mapped[int] = mapped_column(Integer, nullable=False)
    serial_to: Mapped[int] = mapped_column(Integer, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)


class Cheque(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "cheques"

    cheque_book_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    bank_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    cheque_number: Mapped[str] = mapped_column(String(50), nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    issue_date: Mapped[date] = mapped_column(Date, nullable=False)
    due_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    status: Mapped[ChequeStatus] = mapped_column(nullable=False)
    is_incoming: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    payee: Mapped[str | None] = mapped_column(String(255), nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "cheque_number", name="uq_cheque_tenant_number"),
        Index("ix_cheques_tenant_status", "tenant_id", "status"),
    )


class Transfer(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "transfers"

    transfer_date: Mapped[date] = mapped_column(Date, nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    from_type: Mapped[str] = mapped_column(String(20), nullable=False)
    from_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    to_type: Mapped[str] = mapped_column(String(20), nullable=False)
    to_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)

    __table_args__ = (Index("ix_transfers_tenant_date", "tenant_id", "transfer_date"),)


class ReceiptVoucher(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "receipt_vouchers"

    receipt_number: Mapped[str] = mapped_column(String(50), nullable=False)
    receipt_date: Mapped[date] = mapped_column(Date, nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    payment_method: Mapped[str] = mapped_column(String(50), nullable=False)
    cash_box_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    bank_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "receipt_number", name="uq_receipt_tenant_number"),
    )


class PaymentVoucher(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payment_vouchers"

    payment_number: Mapped[str] = mapped_column(String(50), nullable=False)
    payment_date: Mapped[date] = mapped_column(Date, nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    payment_method: Mapped[str] = mapped_column(String(50), nullable=False)
    cash_box_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    bank_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "payment_number", name="uq_payment_tenant_number"),
    )


class Reconciliation(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "reconciliations"

    bank_account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    statement_date: Mapped[date] = mapped_column(Date, nullable=False)
    statement_balance: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    book_balance: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    difference: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    status: Mapped[ReconciliationStatus] = mapped_column(nullable=False)

    __table_args__ = (
        Index("ix_reconciliation_tenant_account", "tenant_id", "bank_account_id"),
    )
