from pathlib import Path

from mints_prep.models import (
    Classification,
    FileRecord,
    FilenameFinding,
    FormatStatus,
    PiiFinding,
    RedactionFinding,
)
from mints_prep.processors.batch_planner import Batch
from mints_prep.processors.output_writer import PlannedCopy
from mints_prep.processors.report_writer import (
    REPORT_FILENAME,
    build_report_text,
    collect_check_warnings,
    collect_critical_warnings,
    format_evidence_sequence_warnings,
    write_report,
)


def _record(
    name: str,
    classification: Classification = Classification.A_COMPLIANT,
    format_status: FormatStatus = FormatStatus.RECORD_READY,
    detected_format: str = "PDF",
    size_bytes: int = 100,
    **kwargs,
) -> FileRecord:
    return FileRecord(
        path=Path(name),
        size_bytes=size_bytes,
        format_status=format_status,
        detected_format=detected_format,
        classification=classification,
        filename_finding=FilenameFinding(
            length=len(name), exceeds_max_length=False, exceeds_warning_length=False
        ),
        **kwargs,
    )


def test_report_includes_summary_counts():
    records = [
        _record("a.pdf", Classification.A_COMPLIANT),
        _record("b.pdf", Classification.B_NEEDS_FIX),
        _record("c.docx", Classification.C_NEEDS_CONVERSION, format_status=FormatStatus.CONVERTIBLE),
        _record("d.zip", Classification.D_UNSUPPORTED, format_status=FormatStatus.UNSUPPORTED),
    ]
    planned = [PlannedCopy(record=records[0], output_filename="a.pdf")]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=records, planned=planned, batches=[]
    )

    assert "合計 4 件" in text
    assert "A：適合済み 1 件" in text
    assert "B：要修正 1 件" in text
    assert "C：要変換 1 件" in text
    assert "D：対象外 1 件" in text


def test_report_prioritizes_oversized_and_redaction_warnings():
    oversized = _record("big.pdf", size_bytes=1, exceeds_single_file_limit=True)
    redacted = _record(
        "secret.pdf",
        redaction_finding=RedactionFinding(has_residual_text=True, pages_with_residual_text=(2,)),
    )
    records = [oversized, redacted]
    planned = [
        PlannedCopy(record=oversized, output_filename="big.pdf"),
        PlannedCopy(record=redacted, output_filename="secret.pdf"),
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=records, planned=planned, batches=[]
    )

    assert "【重要警告（優先対応が必要）】" in text
    assert "big.pdf" in text.split("【重要警告")[1].split("【ファイルごと")[0]
    assert "2ページ目" in text


def test_report_prioritizes_pii_detection_warning():
    """要件定義書4.8：個人情報パターンの検出は墨消し検出と並ぶ最重要警告とする。"""
    flagged = _record(
        "secret.pdf",
        pii_finding=PiiFinding(has_pii=True, detected_categories=("メールアドレス",)),
    )
    planned = [PlannedCopy(record=flagged, output_filename="secret.pdf")]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[flagged], planned=planned, batches=[]
    )

    assert "【重要警告（優先対応が必要）】" in text
    critical_section = text.split("【重要警告")[1].split("【ファイルごと")[0]
    assert "secret.pdf" in critical_section
    assert "個人情報の可能性がある記載を検出しました" in critical_section


def test_report_lists_applied_processing_per_file():
    record = _record("甲001 契約書.pdf")
    planned = [
        PlannedCopy(
            record=record,
            output_filename="甲001 契約書.pdf",
            strip_metadata=True,
            stamp_label="甲001",
        )
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[record], planned=planned, batches=[]
    )

    assert "証拠番号スタンプ（甲001）" in text
    assert "メタデータ除去" in text


def test_report_shows_detected_before_applied_in_chronological_order():
    from mints_prep.models import MetadataFinding

    # describe_expected_processing はA（適合済み）の場合「適合済み（処理不要）」を
    # 返し、これはreport_writer側でフィルタされ「検出事項」行が出ない。検出事項
    # が実際に出るB（要修正）分類で検証する。
    record = _record(
        "甲001 契約書.pdf",
        classification=Classification.B_NEEDS_FIX,
        metadata_finding=MetadataFinding(has_metadata=True, detected_fields=("Author",)),
    )
    planned = [
        PlannedCopy(
            record=record,
            output_filename="甲001 契約書.pdf",
            strip_metadata=True,
            stamp_label="甲001",
        )
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[record], planned=planned, batches=[]
    )

    detected_index = text.index("検出事項：")
    applied_index = text.index("適用処理：")
    assert detected_index < applied_index


def test_report_shows_batch_composition_when_split(tmp_path: Path):
    r1 = _record("a.pdf")
    r2 = _record("b.pdf")
    planned = [
        PlannedCopy(record=r1, output_filename="a.pdf"),
        PlannedCopy(record=r2, output_filename="b.pdf"),
    ]
    batches = [
        Batch(index=1, items=(planned[0],)),
        Batch(index=2, items=(planned[1],)),
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[r1, r2], planned=planned, batches=batches
    )

    assert "【提出回ごとの内訳】" in text
    assert "提出用_01：1 件" in text
    assert "提出用_02：1 件" in text


def test_report_omits_batch_section_when_single_batch():
    r1 = _record("a.pdf")
    planned = [PlannedCopy(record=r1, output_filename="a.pdf")]
    batches = [Batch(index=1, items=(planned[0],))]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[r1], planned=planned, batches=batches
    )

    assert "【提出回ごとの内訳】" not in text


def test_report_lists_unsupported_and_convertible_files():
    records = [
        _record(
            "legacy.docx",
            Classification.C_NEEDS_CONVERSION,
            format_status=FormatStatus.CONVERTIBLE,
            detected_format="DOCX",
        ),
        _record(
            "archive.zip",
            Classification.D_UNSUPPORTED,
            format_status=FormatStatus.UNSUPPORTED,
            detected_format="unknown",
        ),
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=records, planned=[], batches=[]
    )

    assert "【未対応形式として除外したファイル】" in text
    assert "legacy.docx（要変換（非対応形式）、検出形式：DOCX）" in text
    assert "archive.zip（処理対象外、検出形式：unknown）" in text


def test_report_includes_evidence_sequence_section_when_gap_detected(config):
    r1 = _record("甲001 契約書.pdf")
    r2 = _record("甲003 領収書.pdf")
    planned = [
        PlannedCopy(record=r1, output_filename="甲001 契約書.pdf"),
        PlannedCopy(record=r2, output_filename="甲003 領収書.pdf"),
    ]

    text = build_report_text(
        input_dir=Path("in"),
        output_dir=Path("out"),
        all_records=[r1, r2],
        planned=planned,
        batches=[],
        evidence_number_config=config.evidence_number,
    )

    assert "【号証の連番・欠番チェック】" in text
    assert "甲：欠番の疑いあり（甲002）" in text


def test_report_omits_evidence_sequence_section_when_no_issue(config):
    r1 = _record("甲001 契約書.pdf")
    planned = [PlannedCopy(record=r1, output_filename="甲001 契約書.pdf")]

    text = build_report_text(
        input_dir=Path("in"),
        output_dir=Path("out"),
        all_records=[r1],
        planned=planned,
        batches=[],
        evidence_number_config=config.evidence_number,
    )

    assert "【号証の連番・欠番チェック】" not in text


def test_report_omits_evidence_sequence_section_when_config_not_supplied():
    r1 = _record("甲001 契約書.pdf")
    r2 = _record("甲003 領収書.pdf")
    planned = [
        PlannedCopy(record=r1, output_filename="甲001 契約書.pdf"),
        PlannedCopy(record=r2, output_filename="甲003 領収書.pdf"),
    ]

    text = build_report_text(
        input_dir=Path("in"), output_dir=Path("out"), all_records=[r1, r2], planned=planned, batches=[]
    )

    assert "【号証の連番・欠番チェック】" not in text


def test_collect_critical_warnings_covers_oversized_redaction_and_pii():
    """要件定義書4.8：collect_critical_warnings()は書き出し完了時の画面表示
    （ui/app.py）とレポート出力の双方から共有される。3種の重要警告を
    それぞれ拾えることを確認する。"""
    oversized = _record("big.pdf", size_bytes=1, exceeds_single_file_limit=True)
    redacted = _record(
        "secret.pdf",
        redaction_finding=RedactionFinding(has_residual_text=True, pages_with_residual_text=(2,)),
    )
    pii = _record(
        "陳述書.pdf", pii_finding=PiiFinding(has_pii=True, detected_categories=("電話番号",))
    )
    compliant = _record("ok.pdf")

    lines = collect_critical_warnings([oversized, redacted, pii, compliant])

    assert any("big.pdf" in line for line in lines)
    assert any("secret.pdf" in line and "2ページ目" in line for line in lines)
    assert any("陳述書.pdf" in line and "電話番号" in line for line in lines)
    assert len(lines) == 3


def test_collect_critical_warnings_empty_when_nothing_critical():
    assert collect_critical_warnings([_record("ok.pdf")]) == []


def test_collect_check_warnings_covers_password_permission_size_and_orientation():
    """書き出し完了時の警告画面（ui/app.py）に、検出のみの要修正事項
    （パスワード・操作制限・用紙寸法・向き不一致）がすべて表示されること。"""
    from mints_prep.models import PdfFinding

    password = _record("locked.pdf", pdf_finding=PdfFinding(has_password=True))
    restricted = _record(
        "登記情報.pdf", pdf_finding=PdfFinding(has_password=False, has_permission_restriction=True)
    )
    letter = _record(
        "letter.pdf",
        pdf_finding=PdfFinding(has_password=False, page_size_judgments=("A4", "非対応")),
    )
    rotated = _record(
        "rotated.pdf",
        pdf_finding=PdfFinding(has_password=False, orientation_mismatch_pages=(1, 3)),
    )
    compliant = _record("ok.pdf", pdf_finding=PdfFinding(has_password=False))
    non_pdf = _record("photo.jpg", detected_format="JPEG")

    lines = collect_check_warnings([password, restricted, letter, rotated, compliant, non_pdf])

    assert any("locked.pdf" in line and "パスワード付き" in line for line in lines)
    assert any("登記情報.pdf" in line and "印刷・コピー" in line for line in lines)
    assert any("letter.pdf" in line and "A4/A3以外" in line and "2ページ目" in line for line in lines)
    assert any("rotated.pdf" in line and "向き" in line and "1、3ページ目" in line for line in lines)
    assert len(lines) == 4


def test_collect_check_warnings_empty_when_all_compliant():
    from mints_prep.models import PdfFinding

    records = [
        _record("ok.pdf", pdf_finding=PdfFinding(has_password=False, page_size_judgments=("A4",))),
        _record("photo.jpg", detected_format="JPEG"),
    ]
    assert collect_check_warnings(records) == []


def test_format_evidence_sequence_warnings_matches_report_output(config):
    filenames = ["甲001 契約書.pdf", "甲003 領収書.pdf"]

    lines = format_evidence_sequence_warnings(filenames, config.evidence_number)

    assert lines == ["・甲：欠番の疑いあり（甲002）"]


def test_format_evidence_sequence_warnings_empty_when_no_issue(config):
    assert format_evidence_sequence_warnings(["甲001 契約書.pdf"], config.evidence_number) == []


def test_write_report_creates_file_in_output_dir(tmp_path: Path):
    dest = write_report("テスト本文", tmp_path)

    assert dest == tmp_path / REPORT_FILENAME
    assert dest.read_text(encoding="utf-8") == "テスト本文"
