from copy import deepcopy
from pathlib import Path
from types import SimpleNamespace

import pytest
from openpyxl.utils.exceptions import InvalidFileException

from app.services.agent_asset_spreadsheet import (
    COMPANY_TRAVEL_EXPENSE_RULE_CODE,
    AgentAssetSpreadsheetManager,
)
from app.services.expense_rule_runtime import ExpenseRuleRuntimeService
from app.services.expense_rule_runtime_models import build_default_expense_rule_catalog


def _spreadsheet_asset() -> SimpleNamespace:
    return SimpleNamespace(
        code=COMPANY_TRAVEL_EXPENSE_RULE_CODE,
        config_json={
            "detail_mode": "spreadsheet",
            "rule_document": {"storage_key": "corrupted.xlsx"},
        },
        current_version="v1.0.0",
        name="临时损坏规则",
    )


def _spreadsheet_version() -> SimpleNamespace:
    return SimpleNamespace(content="", version="v1.0.0")


def _assert_unreadable_workbook_is_skipped(
    monkeypatch: pytest.MonkeyPatch,
    workbook_path: Path,
) -> None:
    monkeypatch.setattr(
        AgentAssetSpreadsheetManager,
        "resolve_storage_path",
        lambda _manager, _storage_key: workbook_path,
    )
    catalog = build_default_expense_rule_catalog()
    default_catalog = deepcopy(catalog)

    ExpenseRuleRuntimeService(db=None)._apply_spreadsheet_runtime_payload(
        catalog,
        asset=_spreadsheet_asset(),
        version=_spreadsheet_version(),
    )

    assert catalog == default_catalog
    assert catalog.travel_policy is not None
    assert catalog.scene_policies


def test_corrupted_spreadsheet_is_skipped_without_breaking_default_catalog(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    corrupted_workbook = tmp_path / "corrupted.xlsx"
    corrupted_workbook.write_bytes(b"not-an-xlsx-archive")

    _assert_unreadable_workbook_is_skipped(monkeypatch, corrupted_workbook)


def test_invalid_spreadsheet_format_is_skipped_without_breaking_catalog(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    invalid_workbook = tmp_path / "invalid.xlsx"
    invalid_workbook.touch()
    monkeypatch.setattr(
        "app.services.expense_rule_runtime.load_workbook",
        lambda *_args, **_kwargs: (_ for _ in ()).throw(
            InvalidFileException("unsupported workbook format")
        ),
    )

    _assert_unreadable_workbook_is_skipped(monkeypatch, invalid_workbook)
