from __future__ import annotations

import pytest

from . import (
    CellAddress,
    DuplicateSheetError,
    InvalidCellAddressError,
    InvalidRowError,
    InvalidSheetError,
    InvalidSpreadsheetError,
    Sheet,
    SheetName,
    Spreadsheet,
    SpreadsheetService,
    SortDirection,
    SyncMode,
)


def test_sheet_value_objects_validate_and_normalize() -> None:
    assert SheetName("  Sales Report ").value == "Sales Report"
    assert CellAddress("b12").value == "B12"
    assert CellAddress("b12").column == "B"
    assert CellAddress("b12").row == 12

    with pytest.raises(InvalidSheetError):
        SheetName("")

    with pytest.raises(InvalidCellAddressError):
        CellAddress("12B")


def test_spreadsheet_service_manages_sheets_and_cells() -> None:
    service = SpreadsheetService()
    spreadsheet = service.create("Q1 Pipeline", sheet_name="Leads", headers=["id", "name", "value"])
    sheet = service.get_sheet(spreadsheet, "Leads")

    service.append_row(sheet, {"id": 1, "name": "Alice", "value": 100})
    service.append_row(sheet, {"id": 2, "name": "Bob", "value": 250})
    service.set_cell(sheet, "C2", 120)

    assert service.get_cell(sheet, "A2") == 1
    assert service.get_cell(sheet, "C2") == 120
    assert sheet.find_rows({"id": 2})[0].values["name"] == "Bob"

    service.sort_rows(sheet, "value", direction=SortDirection.DESC)
    assert [row.values["id"] for row in sheet.rows] == [2, 1]


def test_spreadsheet_upsert_and_sync_rows_follow_business_rules() -> None:
    service = SpreadsheetService()
    spreadsheet = Spreadsheet(title="Inventory")
    sheet = Sheet(name=SheetName("Products"), headers=["id", "name", "stock"])
    spreadsheet.add_sheet(sheet)

    service.sync_rows(
        sheet,
        [
            {"id": 1, "name": "Keyboard", "stock": 10},
            {"id": 2, "name": "Mouse", "stock": 5},
        ],
        mode=SyncMode.APPEND,
    )
    service.sync_rows(
        sheet,
        [
            {"id": 2, "name": "Mouse", "stock": 7},
            {"id": 3, "name": "Monitor", "stock": 2},
        ],
        mode=SyncMode.UPSERT,
    )

    assert [row.values["stock"] for row in sheet.find_rows({"id": 2})] == [7]
    assert sheet.find_rows({"id": 3})[0].values["name"] == "Monitor"


def test_spreadsheet_rejects_duplicate_sheet_names() -> None:
    spreadsheet = Spreadsheet(title="Duplicate Guard")
    spreadsheet.add_sheet(Sheet(name=SheetName("SheetA")))

    with pytest.raises(DuplicateSheetError):
        spreadsheet.add_sheet(Sheet(name=SheetName("SheetA")))

    with pytest.raises(InvalidSpreadsheetError):
        Spreadsheet(title="Bad", active_sheet="Missing")


def test_sheet_validation_and_cell_boundaries() -> None:
    sheet = Sheet(name=SheetName("Orders"), headers=["id", "amount"])

    with pytest.raises(InvalidRowError):
        sheet.append_row([1])

    with pytest.raises(InvalidSheetError):
        sheet.sort_rows("missing")

    sheet.set_cell("A1", "order_id")
    assert sheet.headers[0] == "order_id"

