from __future__ import annotations

import argparse
import importlib.util
from datetime import UTC, datetime
from pathlib import Path

import pytest


def load_script_module():
    script_path = (
        Path(__file__).resolve().parents[1] / "scripts" / "backfill_legacy_expense_claim_cases.py"
    )
    spec = importlib.util.spec_from_file_location(
        "backfill_legacy_expense_claim_cases_cli",
        script_path,
    )
    assert spec is not None and spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


MODULE = load_script_module()


def test_created_before_requires_timezone_and_normalizes_to_utc() -> None:
    assert MODULE.parse_created_before("2026-07-14T08:00:00+08:00") == datetime(
        2026,
        7,
        14,
        tzinfo=UTC,
    )
    with pytest.raises(argparse.ArgumentTypeError, match="时区"):
        MODULE.parse_created_before("2026-07-14T00:00:00")


@pytest.mark.parametrize("value", ["0", "-1", "not-a-number"])
def test_positive_int_rejects_invalid_values(value: str) -> None:
    with pytest.raises(argparse.ArgumentTypeError):
        MODULE.positive_int(value)


def test_parser_defaults_to_dry_run_and_requires_explicit_scope() -> None:
    args = MODULE.build_parser().parse_args(
        [
            "--tenant-id",
            "default",
            "--created-before",
            "2026-07-14T00:00:00Z",
            "--expected-host",
            "migration-probe",
            "--expected-database",
            "migration_probe",
        ]
    )

    assert args.apply is False
    assert args.dry_run is False
    assert args.batch_size == 100
    assert args.tenant_id == "default"


def test_parser_rejects_apply_and_dry_run_together() -> None:
    with pytest.raises(SystemExit):
        MODULE.build_parser().parse_args(
            [
                "--apply",
                "--dry-run",
                "--tenant-id",
                "default",
                "--created-before",
                "2026-07-14T00:00:00Z",
                "--expected-host",
                "migration-probe",
                "--expected-database",
                "migration_probe",
            ]
        )


def test_parser_rejects_blank_tenant() -> None:
    with pytest.raises(SystemExit):
        MODULE.build_parser().parse_args(
            [
                "--tenant-id",
                " ",
                "--created-before",
                "2026-07-14T00:00:00Z",
                "--expected-host",
                "migration-probe",
                "--expected-database",
                "migration_probe",
            ]
        )


def test_apply_requires_exact_target_confirmation_before_connecting(monkeypatch) -> None:
    monkeypatch.setenv(
        "DATABASE_URL",
        "postgresql://finance:secret@migration-probe/migration_probe",
    )
    args = MODULE.build_parser().parse_args(
        [
            "--apply",
            "--tenant-id",
            "default",
            "--created-before",
            "2026-07-14T00:00:00Z",
            "--expected-host",
            "migration-probe",
            "--expected-database",
            "migration_probe",
        ]
    )

    with pytest.raises(MODULE.BackfillCommandError) as exc_info:
        MODULE.run(args)

    assert exc_info.value.code == "confirm_target_required"
    assert exc_info.value.exit_code == MODULE.EXIT_SAFETY


def test_missing_database_url_is_rejected_without_configuration_fallback(monkeypatch) -> None:
    monkeypatch.delenv("DATABASE_URL", raising=False)
    args = MODULE.build_parser().parse_args(
        [
            "--tenant-id",
            "default",
            "--created-before",
            "2026-07-14T00:00:00Z",
            "--expected-host",
            "migration-probe",
            "--expected-database",
            "migration_probe",
        ]
    )

    with pytest.raises(MODULE.MaintenanceDatabaseTargetError) as exc_info:
        MODULE.run(args)

    assert exc_info.value.code == "database_url_required"


def test_partial_failure_payload_discloses_committed_progress() -> None:
    summary = {
        "run_id": "historical-import-run-1",
        "batches": 2,
        "inspected": 4,
        "created": 3,
        "already_linked": 1,
        "last_cursor": {
            "created_at": "2026-07-10T00:00:00Z",
            "claim_id": "claim-4",
        },
    }
    details = MODULE._execution_progress(
        summary,
        current_batch_conflict_ids=["claim-5"],
    )
    error = MODULE.BackfillCommandError(
        "current batch rolled back",
        exit_code=MODULE.EXIT_CONFLICT,
        code="legacy_claim_conflict",
        details=details,
    )

    payload = MODULE._error_payload(error, code=error.code, details=error.details)

    assert payload["details"] == {
        "run_id": "historical-import-run-1",
        "partial_commit": True,
        "committed_batches": 2,
        "committed_claims": 4,
        "created": 3,
        "already_linked": 1,
        "last_committed_cursor": {
            "created_at": "2026-07-10T00:00:00Z",
            "claim_id": "claim-4",
        },
        "current_batch_conflict_ids": ["claim-5"],
    }
