"""Phase 5.7 — Purchase & Inventory accounting domain models."""
from __future__ import annotations

import uuid
from datetime import date
from decimal import Decimal

from sqlalchemy import Boolean, Date, 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, InventoryValuationMethod


class PurchasePostingProfile(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "purchase_posting_profiles"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    inventory_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    grni_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    expense_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    liability_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    discount_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    adjustment_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_default: 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", "name", name="uq_purchase_profile_tenant_name"),)


class InventoryPostingProfile(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_posting_profiles"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    inventory_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    cogs_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    adjustment_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    gain_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    loss_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    write_off_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_default: 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", "name", name="uq_inventory_profile_tenant_name"),)


class InventoryValuationMethodConfig(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_valuation_methods"

    item_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    warehouse_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    method: Mapped[InventoryValuationMethod] = mapped_column(nullable=False)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        Index("ix_inv_valuation_tenant_item", "tenant_id", "item_id"),
    )


class InventoryValuationHistory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_valuation_history"

    item_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    warehouse_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    valuation_date: Mapped[date] = mapped_column(Date, nullable=False)
    method: Mapped[InventoryValuationMethod] = mapped_column(nullable=False)
    quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    unit_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    total_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)

    __table_args__ = (
        Index("ix_inv_val_history_item", "tenant_id", "item_id", "valuation_date"),
    )


class InventoryCostAdjustment(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_cost_adjustments"

    item_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    warehouse_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    adjustment_date: Mapped[date] = mapped_column(Date, nullable=False)
    old_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    new_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    difference: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)


class PurchaseAccountingConfiguration(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "purchase_accounting_configurations"

    default_profile_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    auto_post_receipts: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    auto_post_invoices: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

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


class InventoryAccountingConfiguration(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_accounting_configurations"

    default_profile_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    default_valuation_method: Mapped[InventoryValuationMethod] = mapped_column(
        default=InventoryValuationMethod.WEIGHTED_AVERAGE, nullable=False
    )
    auto_post_issues: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

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


class InventorySnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "inventory_snapshots"

    item_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    warehouse_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    snapshot_date: Mapped[date] = mapped_column(Date, nullable=False)
    quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    unit_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    total_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)

    __table_args__ = (
        Index("ix_inventory_snapshot_item", "tenant_id", "item_id", "snapshot_date"),
    )
