"""Workbook state management.

Owns the IronCalc model instance and provides a clean interface
for tool modules. No tool should import ironcalc directly.
"""

from __future__ import annotations

import ironcalc
from pydantic import BaseModel, ConfigDict, PrivateAttr


class CellValue(BaseModel):
    row: int
    column: int
    content: str
    formatted: str
    cell_type: str


class SheetInfo(BaseModel):
    index: int
    name: str
    state: str
    color: str | None
    dimensions: tuple[int, int, int, int]


class WorkbookState(BaseModel):
    """Holds and manages a single IronCalc model."""

    model_config = ConfigDict(arbitrary_types_allowed=True)

    _model: ironcalc.PyModel | None = PrivateAttr(default=None)  # ty: ignore[unresolved-attribute]
    _file_path: str | None = PrivateAttr(default=None)

    @property
    def model(self) -> ironcalc.PyModel:  # ty: ignore[unresolved-attribute]
        if self._model is None:
            raise RuntimeError("No workbook loaded. Use create_workbook or load_workbook first.")
        return self._model

    @property
    def is_loaded(self) -> bool:
        return self._model is not None

    @property
    def file_path(self) -> str | None:
        return self._file_path

    def create(
        self,
        name: str = "Workbook",
        locale: str = "en",
        timezone: str = "UTC",
        language: str = "en",
    ) -> None:
        self._model = ironcalc.create(name, locale, timezone, language)  # ty: ignore[unresolved-attribute]
        self._model.evaluate()
        self._file_path = None

    def load_xlsx(
        self,
        file_path: str,
        locale: str = "en",
        timezone: str = "UTC",
        language: str = "en",
    ) -> None:
        self._model = ironcalc.load_from_xlsx(file_path, locale, timezone, language)  # ty: ignore[unresolved-attribute]
        self._model.evaluate()
        self._file_path = file_path

    def save_xlsx(self, file_path: str | None = None) -> str:
        path = file_path or self._file_path
        if path is None:
            raise ValueError("No file path specified and no previous path to save to.")
        self.model.save_to_xlsx(path)
        self._file_path = path
        return path

    def get_sheets(self) -> list[SheetInfo]:
        props = self.model.get_worksheets_properties()
        sheets = []
        for i, prop in enumerate(props):
            dims = self.model.get_sheet_dimensions(i)
            sheets.append(SheetInfo(
                index=i,
                name=prop.name,
                state=prop.state,
                color=prop.color,
                dimensions=dims,
            ))
        return sheets

    def get_cell(self, sheet: int, row: int, column: int) -> CellValue:
        return CellValue(
            row=row,
            column=column,
            content=self.model.get_cell_content(sheet, row, column),
            formatted=self.model.get_formatted_cell_value(sheet, row, column),
            cell_type=str(self.model.get_cell_type(sheet, row, column)).removeprefix("PyCellType.").lower(),
        )

    def get_cells_in_range(
        self, sheet: int, row_start: int, col_start: int, row_end: int, col_end: int
    ) -> list[CellValue]:
        cells = []
        for r in range(row_start, row_end + 1):
            for c in range(col_start, col_end + 1):
                cells.append(self.get_cell(sheet, r, c))
        return cells

    def set_cell(self, sheet: int, row: int, column: int, value: str) -> None:
        self.model.set_user_input(sheet, row, column, value)
        self.model.evaluate()

    def set_cells_batch(self, cells: list[dict]) -> int:
        """Set multiple cells. Each dict has sheet, row, column, value."""
        for cell in cells:
            self.model.set_user_input(cell["sheet"], cell["row"], cell["column"], cell["value"])
        self.model.evaluate()
        return len(cells)


# Singleton state for the server
workbook = WorkbookState()
