"""Phase 5.1 — Accounting foundation domain models."""
from __future__ import annotations

import uuid
from datetime import date, datetime
from decimal import Decimal

from sqlalchemy import (
    Boolean,
    Date,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    Numeric,
    String,
    Text,
    UniqueConstraint,
    func,
)
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 (
    AccountCategory,
    AccountStatus,
    AccountType,
    FiscalPeriodStatus,
    GUID,
)


class ChartOfAccounts(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "chart_of_accounts"

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

    accounts: Mapped[list["Account"]] = relationship(back_populates="chart")

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


class Account(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "accounts"

    chart_id: Mapped[uuid.UUID] = mapped_column(
        GUID(), ForeignKey("chart_of_accounts.id", ondelete="CASCADE"), nullable=False
    )
    parent_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), ForeignKey("accounts.id", ondelete="SET NULL"), nullable=True
    )
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    account_type: Mapped[AccountType] = mapped_column(nullable=False)
    account_category: Mapped[AccountCategory] = mapped_column(nullable=False)
    level: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    is_group: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_postable: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    status: Mapped[AccountStatus] = mapped_column(
        default=AccountStatus.ACTIVE, nullable=False
    )
    currency_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    chart: Mapped["ChartOfAccounts"] = relationship(back_populates="accounts")
    parent: Mapped["Account | None"] = relationship(remote_side="Account.id")

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_account_tenant_code"),
        Index("ix_accounts_chart_id", "chart_id"),
    )


class Currency(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "currencies"

    code: Mapped[str] = mapped_column(String(3), nullable=False)
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    symbol: Mapped[str | None] = mapped_column(String(10), nullable=True)
    decimal_places: Mapped[int] = mapped_column(Integer, default=2, nullable=False)
    is_base: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class ExchangeRate(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "exchange_rates"

    from_currency_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    to_currency_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    rate: Mapped[Decimal] = mapped_column(Numeric(18, 8), nullable=False)
    effective_date: Mapped[date] = mapped_column(Date, nullable=False)

    __table_args__ = (
        Index("ix_exchange_rates_tenant_date", "tenant_id", "effective_date"),
    )


class FiscalYear(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "fiscal_years"

    name: Mapped[str] = mapped_column(String(100), nullable=False)
    start_date: Mapped[date] = mapped_column(Date, nullable=False)
    end_date: Mapped[date] = mapped_column(Date, nullable=False)
    is_current: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_closed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    periods: Mapped[list["FiscalPeriod"]] = relationship(back_populates="fiscal_year")

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


class FiscalPeriod(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "fiscal_periods"

    fiscal_year_id: Mapped[uuid.UUID] = mapped_column(
        GUID(), ForeignKey("fiscal_years.id", ondelete="CASCADE"), nullable=False
    )
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    period_number: Mapped[int] = mapped_column(Integer, nullable=False)
    start_date: Mapped[date] = mapped_column(Date, nullable=False)
    end_date: Mapped[date] = mapped_column(Date, nullable=False)
    status: Mapped[FiscalPeriodStatus] = mapped_column(
        default=FiscalPeriodStatus.OPEN, nullable=False
    )
    is_current: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    fiscal_year: Mapped["FiscalYear"] = relationship(back_populates="periods")

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "fiscal_year_id", "period_number",
            name="uq_fiscal_period_tenant_year_num",
        ),
        Index("ix_fiscal_periods_tenant_status", "tenant_id", "status"),
    )


class CostCenter(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "cost_centers"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    parent_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class Project(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "projects"

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

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


class Dimension(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "dimensions"

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

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


class AccountingSettings(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "accounting_settings"

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

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


class DocumentNumberSequence(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "document_number_sequences"

    document_type: Mapped[str] = mapped_column(String(50), nullable=False)
    prefix: Mapped[str | None] = mapped_column(String(20), nullable=True)
    next_number: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    padding: Mapped[int] = mapped_column(Integer, default=6, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "document_type", name="uq_doc_seq_tenant_type"
        ),
    )


class TenantAccountingConfiguration(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "tenant_accounting_configurations"

    base_currency_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    default_chart_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    fiscal_year_start_month: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    allow_multi_currency: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    require_cost_center: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    require_project: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

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