from pathlib import Path

from fixtures.generate_fixtures import make_a4_pdf
from mints_prep.config import EvidenceNumberConfig, FilenameConfig
from mints_prep.models import Classification, FileRecord, FilenameFinding, FormatStatus, MetadataFinding
from mints_prep.processors.evidence_merger import find_branch_merge_groups
from mints_prep.processors.output_writer import PlannedCopy, plan_output_filenames
from mints_prep.ui.review_logic import ReviewItem, apply_review_choices, build_review_options

_FILENAME_CONFIG = FilenameConfig(
    max_length=100,
    warning_length=50,
    forbidden_chars=("\\", "/", ":", "*", "?", '"', "<", ">", "|"),
    jis_x0213_codec="shift_jis_2004",
)
_EVIDENCE_CONFIG = EvidenceNumberConfig(symbols=("甲", "乙", "丙"), digits=3)


def _record(
    name: str,
    exceeds_max_length: bool = False,
    forbidden_chars_found: tuple[str, ...] = (),
    has_metadata: bool = False,
    detected_format: str = "PDF",
) -> FileRecord:
    return FileRecord(
        path=Path(name),
        size_bytes=100,
        format_status=FormatStatus.RECORD_READY,
        detected_format=detected_format,
        classification=Classification.A_COMPLIANT,
        filename_finding=FilenameFinding(
            length=len(name),
            exceeds_max_length=exceeds_max_length,
            exceeds_warning_length=False,
            forbidden_chars_found=forbidden_chars_found,
        ),
        metadata_finding=MetadataFinding(has_metadata=has_metadata, detected_fields=("Author",) if has_metadata else ()),
    )


def _item(prelim: PlannedCopy) -> ReviewItem:
    return ReviewItem(preliminary=prelim, preview_record=prelim.record, batch_dir=Path("out"))


def test_build_review_options_empty_when_nothing_applicable():
    record = _record("甲001 契約書.pdf")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    assert options == []


def test_build_review_options_includes_sanitize_when_needed():
    record = _record("bad<name>.pdf", forbidden_chars_found=("<", ">"))
    prelim = PlannedCopy(record=record, output_filename="bad_name_.pdf")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    keys = [o.key for o in options]
    assert "sanitize" in keys
    sanitize_opt = next(o for o in options if o.key == "sanitize")
    assert sanitize_opt.default_checked is True
    assert "bad<name>.pdf" in sanitize_opt.label
    assert "bad_name_.pdf" in sanitize_opt.label


def test_build_review_options_includes_strip_metadata_when_needed():
    record = _record("甲001.pdf", has_metadata=True)
    prelim = PlannedCopy(record=record, output_filename="甲001.pdf", strip_metadata=True)

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    opt = next(o for o in options if o.key == "strip_metadata")
    assert opt.default_checked is True
    assert "Author" in opt.label


def test_build_review_options_includes_stamp_for_standalone_evidence_match():
    record = _record("甲001 契約書.pdf")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf", stamp_label="甲001")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    opt = next(o for o in options if o.key == "stamp")
    assert opt.default_checked is True
    assert "甲001" in opt.label


def test_build_review_options_omits_stamp_when_no_evidence_match():
    record = _record("準備書面1.pdf")
    prelim = PlannedCopy(record=record, output_filename="準備書面1.pdf")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    assert all(o.key != "stamp" for o in options)


def test_build_review_options_stamp_defaults_off_for_image_formats():
    # PDF以外（JPEG/PNG）は、証拠番号スタンプの既定値をOFFとする
    # （写真等は原本の見た目そのものが証拠内容と直結し得るため）。
    record = _record("甲001 現場写真.jpg", detected_format="JPEG")
    prelim = PlannedCopy(record=record, output_filename="甲001 現場写真.jpg", stamp_label="甲001")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    opt = next(o for o in options if o.key == "stamp")
    assert opt.default_checked is False


def test_build_review_options_stamp_defaults_on_for_pdf():
    record = _record("甲001 契約書.pdf", detected_format="PDF")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf", stamp_label="甲001")

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    opt = next(o for o in options if o.key == "stamp")
    assert opt.default_checked is True


def _merge_preliminary(records: list[FileRecord]) -> PlannedCopy:
    groups = find_branch_merge_groups(records, _EVIDENCE_CONFIG)
    planned = plan_output_filenames(
        [], {}, merge_groups=groups, stamp_merge_sources=True, filename_config=_FILENAME_CONFIG
    )
    return planned[0]


def test_build_review_options_for_merge_group_shows_branch_range(tmp_path: Path):
    records = [
        _record_with_real_file(tmp_path, "甲001-1 領収書.pdf"),
        _record_with_real_file(tmp_path, "甲001-2 領収書.pdf"),
    ]
    prelim = _merge_preliminary(records)

    options = build_review_options(_item(prelim), _FILENAME_CONFIG)

    opt = next(o for o in options if o.key == "stamp")
    assert "甲001-1" in opt.label and "甲001-2" in opt.label


def _record_with_real_file(tmp_path: Path, name: str) -> FileRecord:
    path = make_a4_pdf(tmp_path / name)
    return FileRecord(
        path=path,
        size_bytes=path.stat().st_size,
        format_status=FormatStatus.RECORD_READY,
        detected_format="PDF",
        classification=Classification.A_COMPLIANT,
        filename_finding=FilenameFinding(
            length=len(name), exceeds_max_length=False, exceeds_warning_length=False
        ),
    )


def test_apply_review_choices_all_checked_matches_preliminary():
    record = _record("bad<name>.pdf", forbidden_chars_found=("<", ">"), has_metadata=True)
    prelim = PlannedCopy(
        record=record, output_filename="bad_name_.pdf", strip_metadata=True, stamp_label="甲001"
    )

    final = apply_review_choices(
        _item(prelim), {"sanitize": True, "strip_metadata": True, "stamp": True}, _FILENAME_CONFIG
    )

    assert final.output_filename == "bad_name_.pdf"
    assert final.strip_metadata is True
    assert final.stamp_label == "甲001"


def test_apply_review_choices_all_unchecked_reverts_to_original():
    record = _record("bad<name>.pdf", forbidden_chars_found=("<", ">"), has_metadata=True)
    prelim = PlannedCopy(
        record=record, output_filename="bad_name_.pdf", strip_metadata=True, stamp_label="甲001"
    )

    final = apply_review_choices(_item(prelim), {}, _FILENAME_CONFIG)

    assert final.output_filename == "bad<name>.pdf"
    assert final.strip_metadata is False
    assert final.stamp_label is None


def test_apply_review_choices_stamp_offset_applied_when_stamp_on():
    record = _record("甲001 契約書.pdf")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf", stamp_label="甲001")

    final = apply_review_choices(
        _item(prelim), {"stamp": True}, _FILENAME_CONFIG, stamp_offset_mm=(20.0, 30.0)
    )

    assert final.stamp_margin_top_mm == 20.0
    assert final.stamp_margin_right_mm == 30.0


def test_apply_review_choices_stamp_offset_ignored_when_stamp_off():
    record = _record("甲001 契約書.pdf")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf", stamp_label="甲001")

    final = apply_review_choices(
        _item(prelim), {"stamp": False}, _FILENAME_CONFIG, stamp_offset_mm=(20.0, 30.0)
    )

    assert final.stamp_margin_top_mm is None
    assert final.stamp_margin_right_mm is None


def test_apply_review_choices_stamp_offset_defaults_to_none_when_not_supplied():
    record = _record("甲001 契約書.pdf")
    prelim = PlannedCopy(record=record, output_filename="甲001 契約書.pdf", stamp_label="甲001")

    final = apply_review_choices(_item(prelim), {"stamp": True}, _FILENAME_CONFIG)

    assert final.stamp_margin_top_mm is None
    assert final.stamp_margin_right_mm is None


def test_apply_review_choices_stamp_offset_applied_to_merge_group(tmp_path: Path):
    records = [
        _record_with_real_file(tmp_path, "甲001-1 領収書.pdf"),
        _record_with_real_file(tmp_path, "甲001-2 領収書.pdf"),
    ]
    prelim = _merge_preliminary(records)

    final = apply_review_choices(
        _item(prelim), {"stamp": True}, _FILENAME_CONFIG, stamp_offset_mm=(15.0, 25.0)
    )

    assert final.stamp_margin_top_mm == 15.0
    assert final.stamp_margin_right_mm == 25.0


def test_apply_review_choices_for_merge_group_toggles_merge_stamp(tmp_path: Path):
    records = [
        _record_with_real_file(tmp_path, "甲001-1 領収書.pdf"),
        _record_with_real_file(tmp_path, "甲001-2 領収書.pdf"),
    ]
    prelim = _merge_preliminary(records)

    final_on = apply_review_choices(_item(prelim), {"stamp": True}, _FILENAME_CONFIG)
    final_off = apply_review_choices(_item(prelim), {"stamp": False}, _FILENAME_CONFIG)

    assert final_on.merge_stamp is True
    assert final_off.merge_stamp is False
    assert final_on.merge_group is prelim.merge_group
    assert final_off.output_filename == prelim.output_filename
