"""
Data models for g-gremlin.
"""

from __future__ import annotations

from dataclasses import asdict, dataclass
from typing import Any, Dict, List, Literal, Mapping, Optional


@dataclass
class SheetState:
    """
    Represents the state of a pulled Google Sheet stored locally.
    """

    spreadsheet_id: str
    range: str
    output_file: str
    concurrency_token: Optional[str] = None
    pulled_at: str = ""

    def to_dict(self) -> Dict[str, Any]:
        """
        Convert the state to a JSON-serializable dict.
        """
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Mapping[str, Any]) -> "SheetState":
        """
        Create a SheetState from a mapping object.
        """
        return cls(
            spreadsheet_id=str(data.get("spreadsheet_id", "")),
            range=str(data.get("range", "")),
            output_file=str(data.get("output_file", "")),
            concurrency_token=data.get("concurrency_token") or None,
            pulled_at=str(data.get("pulled_at", "")),
        )


@dataclass
class CellChange:
    """
    Represents a single cell value change.
    """

    row_key: str
    row_index: int  # 0-based data row index, excluding header
    column_name: str
    column_index: int  # 0-based column index
    old: Optional[str]
    new: Optional[str]


@dataclass
class RowDiff:
    """
    Represents all cell changes for a given row.
    """

    row_key: str
    row_index: int
    changes: List[CellChange]


@dataclass
class DiffSummary:
    """
    Summary counts for a diff operation.
    """

    added_rows: int
    removed_rows: int
    changed_rows: int
    changed_cells: int


@dataclass
class SheetDiff:
    """
    Full diff model capturing row-level changes and additions/removals.
    """

    summary: DiffSummary
    row_diffs: List[RowDiff]
    added_keys: List[str]
    removed_keys: List[str]


ColumnType = Literal["number", "text", "boolean", "date", "datetime", "mixed", "empty"]
ColumnRole = Literal["dimension", "measure", "derived", "unknown"]


@dataclass
class TabClassification:
    """
    Heuristic classification of a tab's grain/type.
    """

    type: str
    confidence: float
    evidence: List[str]
    suggested_grain: Optional[str] = None
    suggested_primary_key: Optional[str] = None


@dataclass
class ColumnProfile:
    """
    Metadata about a single column for inspection.
    """

    name: str
    index: int  # 0-based
    inferred_type: ColumnType
    non_null_count: int
    distinct_count: int
    sample_values: List[str]
    has_formula: bool
    role: ColumnRole
    semantic_type: Optional[str] = None
    validation: Optional[Dict[str, Any]] = None


@dataclass
class FormulaCell:
    """
    Captures the location and contents of a formula cell.
    """

    a1: str
    row_index: int  # 0-based data row index (excluding header)
    column_index: int  # 0-based column index
    formula: str
    value: Optional[str]
    is_error: bool


@dataclass
class SheetProfile:
    """
    Read-only profile of a sheet for AI consumption.
    """

    spreadsheet_id: str
    sheet_name: str
    a1_range: str
    header: List[str]
    row_count: int  # data rows, excluding header
    column_profiles: List[ColumnProfile]
    formula_cells: List[FormulaCell]
    named_ranges: List["NamedRangeProfile"]
    classification: Optional[TabClassification] = None
    detected_sfdc_patterns: Optional[Dict[str, Any]] = None
    structural_profile: Optional[Dict[str, Any]] = None


@dataclass
class NamedRangeProfile:
    """
    Captures a named range reference.
    """

    name: str
    range_a1: str
