"""Core business entities with temporal modeling."""

from datetime import date, datetime
from decimal import Decimal
from typing import Any

from pydantic import BaseModel, ConfigDict, Field

from .enums import (
    DocumentKind,
    ExpenseType,
    IncomeType,
    PartySubtype,
    PropertyUsage,
    TaxpayerType,
)


class BaseEntity(BaseModel):
    """Base entity with temporal fields"""

    model_config = ConfigDict(
        str_strip_whitespace=True, validate_assignment=True, use_enum_values=True
    )

    # Temporal fields (bitemporal modeling)
    valid_from: datetime = Field(
        ..., description="When the fact became valid in reality"
    )
    valid_to: datetime | None = Field(
        None, description="When the fact ceased to be valid"
    )
    asserted_at: datetime = Field(
        default_factory=datetime.utcnow, description="When recorded in system"
    )
    retracted_at: datetime | None = Field(
        None, description="When retracted from system"
    )
    source: str = Field(..., description="Source of the information")
    extractor_version: str = Field(..., description="Version of extraction system")


class TaxpayerProfile(BaseEntity):
    """Taxpayer profile entity"""

    taxpayer_id: str = Field(..., description="Unique taxpayer identifier")
    type: TaxpayerType = Field(..., description="Type of taxpayer")
    utr: str | None = Field(
        None, pattern=r"^\d{10}$", description="Unique Taxpayer Reference"
    )
    ni_number: str | None = Field(
        None,
        pattern=r"^[A-CEGHJ-PR-TW-Z]{2}\d{6}[A-D]$",
        description="National Insurance Number",
    )
    residence: str | None = Field(None, description="Tax residence")


class Document(BaseEntity):
    """Document entity"""

    doc_id: str = Field(
        ..., pattern=r"^doc_[a-f0-9]{16}$", description="Document identifier"
    )
    kind: DocumentKind = Field(..., description="Type of document")
    source: str = Field(..., description="Source of document")
    mime: str = Field(..., description="MIME type")
    checksum: str = Field(
        ..., pattern=r"^[a-f0-9]{64}$", description="SHA-256 checksum"
    )
    file_size: int | None = Field(None, ge=0, description="File size in bytes")
    pages: int | None = Field(None, ge=1, description="Number of pages")
    date_range: dict[str, date] | None = Field(None, description="Document date range")


class Evidence(BaseEntity):
    """Evidence entity linking to document snippets"""

    snippet_id: str = Field(..., description="Evidence snippet identifier")
    doc_ref: str = Field(..., description="Reference to source document")
    page: int = Field(..., ge=1, description="Page number")
    bbox: list[float] | None = Field(
        None, description="Bounding box coordinates [x1, y1, x2, y2]"
    )
    text_hash: str = Field(
        ..., pattern=r"^[a-f0-9]{64}$", description="SHA-256 hash of extracted text"
    )
    ocr_confidence: float | None = Field(
        None, ge=0.0, le=1.0, description="OCR confidence score"
    )


class IncomeItem(BaseEntity):
    """Income item entity"""

    income_id: str = Field(..., description="Income item identifier")
    type: IncomeType = Field(..., description="Type of income")
    gross: Decimal = Field(..., ge=0, description="Gross amount")
    net: Decimal | None = Field(None, ge=0, description="Net amount")
    tax_withheld: Decimal | None = Field(None, ge=0, description="Tax withheld")
    currency: str = Field(..., pattern=r"^[A-Z]{3}$", description="Currency code")
    period_start: date | None = Field(None, description="Income period start")
    period_end: date | None = Field(None, description="Income period end")
    description: str | None = Field(None, description="Income description")


class ExpenseItem(BaseEntity):
    """Expense item entity"""

    expense_id: str = Field(..., description="Expense item identifier")
    type: ExpenseType = Field(..., description="Type of expense")
    amount: Decimal = Field(..., ge=0, description="Expense amount")
    currency: str = Field(..., pattern=r"^[A-Z]{3}$", description="Currency code")
    description: str | None = Field(None, description="Expense description")
    category: str | None = Field(None, description="Expense category")
    allowable: bool | None = Field(None, description="Whether expense is allowable")
    capitalizable_flag: bool | None = Field(
        None, description="Whether expense should be capitalized"
    )
    vat_amount: Decimal | None = Field(None, ge=0, description="VAT amount")
    net_amount: Decimal | None = Field(
        None, ge=0, description="Net amount excluding VAT"
    )


class Party(BaseEntity):
    """Party entity (person or organization)"""

    party_id: str = Field(..., description="Party identifier")
    name: str = Field(..., min_length=1, description="Party name")
    subtype: PartySubtype | None = Field(None, description="Party subtype")
    address: str | None = Field(None, description="Party address")
    vat_number: str | None = Field(
        None, pattern=r"^GB\d{9}$|^GB\d{12}$", description="UK VAT number"
    )
    utr: str | None = Field(
        None, pattern=r"^\d{10}$", description="Unique Taxpayer Reference"
    )
    reg_no: str | None = Field(None, description="Registration number")
    paye_reference: str | None = Field(None, description="PAYE reference")


class Account(BaseEntity):
    """Bank account entity"""

    account_id: str = Field(..., description="Account identifier")
    iban: str | None = Field(
        None, pattern=r"^GB\d{2}[A-Z]{4}\d{14}$", description="UK IBAN"
    )
    sort_code: str | None = Field(
        None, pattern=r"^\d{2}-\d{2}-\d{2}$", description="Sort code"
    )
    account_no: str | None = Field(
        None, pattern=r"^\d{8}$", description="Account number"
    )
    institution: str | None = Field(None, description="Financial institution")
    account_type: str | None = Field(None, description="Account type")
    currency: str = Field(default="GBP", description="Account currency")


class PropertyAsset(BaseEntity):
    """Property asset entity"""

    property_id: str = Field(..., description="Property identifier")
    address: str = Field(..., min_length=10, description="Property address")
    postcode: str | None = Field(
        None, pattern=r"^[A-Z]{1,2}\d[A-Z0-9]?\s*\d[A-Z]{2}$", description="UK postcode"
    )
    tenure: str | None = Field(None, description="Property tenure")
    ownership_share: float | None = Field(
        None, ge=0.0, le=1.0, description="Ownership share"
    )
    usage: PropertyUsage | None = Field(None, description="Property usage type")


class Payment(BaseEntity):
    """Payment transaction entity"""

    payment_id: str = Field(..., description="Payment identifier")
    payment_date: date = Field(..., description="Payment date")
    amount: Decimal = Field(
        ..., description="Payment amount (positive for credit, negative for debit)"
    )
    currency: str = Field(..., pattern=r"^[A-Z]{3}$", description="Currency code")
    direction: str = Field(..., description="Payment direction (credit/debit)")
    description: str | None = Field(None, description="Payment description")
    reference: str | None = Field(None, description="Payment reference")
    balance_after: Decimal | None = Field(
        None, description="Account balance after payment"
    )


class Calculation(BaseEntity):
    """Tax calculation entity"""

    calculation_id: str = Field(..., description="Calculation identifier")
    schedule: str = Field(..., description="Tax schedule (SA100, SA103, etc.)")
    tax_year: str = Field(
        ..., pattern=r"^\d{4}-\d{2}$", description="Tax year (e.g., 2023-24)"
    )
    total_income: Decimal | None = Field(None, ge=0, description="Total income")
    total_expenses: Decimal | None = Field(None, ge=0, description="Total expenses")
    net_profit: Decimal | None = Field(None, description="Net profit/loss")
    calculated_at: datetime = Field(
        default_factory=datetime.utcnow, description="Calculation timestamp"
    )


class FormBox(BaseEntity):
    """Form box entity"""

    form: str = Field(..., description="Form identifier (SA100, SA103, etc.)")
    box: str = Field(..., description="Box identifier")
    value: Decimal | str | bool = Field(..., description="Box value")
    description: str | None = Field(None, description="Box description")
    confidence: float | None = Field(
        None, ge=0.0, le=1.0, description="Confidence score"
    )


class Rule(BaseEntity):
    """Tax rule entity"""

    rule_id: str = Field(..., description="Rule identifier")
    name: str = Field(..., description="Rule name")
    description: str | None = Field(None, description="Rule description")
    jurisdiction: str = Field(default="UK", description="Tax jurisdiction")
    tax_years: list[str] = Field(..., description="Applicable tax years")
    formula: str | None = Field(None, description="Rule formula")
    conditions: dict[str, Any] | None = Field(None, description="Rule conditions")
