from __future__ import annotations

import importlib.util
import json
import stat
import sys
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "prepare_telegram_export_corpus.py"


def load_module():
    spec = importlib.util.spec_from_file_location("prepare_telegram_export_corpus", SCRIPT)
    assert spec and spec.loader
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module
    spec.loader.exec_module(module)
    return module


def export_html(attachment_href: str = "files/evidence.txt") -> str:
    return f"""<!doctype html><html><body>
    <div class="page_header"><div class="text bold">Leo</div></div>
    <div class="message service" id="message-1"><div class="body details">1 July 2026</div></div>
    <div class="message default clearfix" id="message101">
      <div class="body"><div class="pull_right date details" title="01.07.2026 10:00:00 UTC+01:00">10:00</div>
      <div class="from_name">m3taversal</div><div class="text">Challenge this claim.<br>It is shallow.
      <a href="https://example.com/source">source</a></div></div>
    </div>
    <div class="message default clearfix joined" id="message102">
      <div class="body"><div class="pull_right date details" title="01.07.2026 10:01:00 UTC+01:00">10:01</div>
      <div class="forwarded body"><div class="from_name">Forwarded Person <span class="date details">now</span></div>
      <div class="text">Use the attached evidence. TWITTERAPI_IO_KEY=fixture-secret-value
      <a href="{attachment_href}">file</a></div></div></div>
    </div>
    <div class="message default clearfix" id="message103">
      <div class="body"><div class="pull_right date details" title="01.07.2026 10:02:00 UTC+01:00">10:02</div>
      <div class="from_name">Leo</div><div class="reply_to details"><a href="#go_to_message101">this message</a></div>
      <div class="text">The body is broader than its evidence.</div></div>
    </div></body></html>"""


def write_export(tmp_path: Path, html: str | None = None) -> Path:
    root = tmp_path / "export"
    (root / "files").mkdir(parents=True)
    (root / "files" / "evidence.txt").write_text("bounded evidence\n", encoding="utf-8")
    path = root / "messages.html"
    path.write_text(html or export_html(), encoding="utf-8")
    return path


def test_prepare_builds_private_hash_bound_segments_without_model_or_database(tmp_path: Path) -> None:
    module = load_module()
    export = write_export(tmp_path)
    output = tmp_path / "private-corpus"

    manifest = module.prepare(export, output, 1000)

    assert manifest["schema"] == module.SCHEMA
    assert manifest["source"]["chat_title"] == "Leo"
    assert manifest["counts"] == {"attachments": 1, "messages": 3, "participants": 2, "segments": 1}
    assert manifest["participants"] == ["Leo", "m3taversal"]
    assert manifest["safety"] == {
        "database_write_performed": False,
        "message_text_in_manifest": False,
        "model_call_performed": False,
        "output_files_private": True,
        "secret_values_removed_before_segments": True,
    }
    segment_path = output / manifest["segments"][0]["path"]
    segment = segment_path.read_text(encoding="utf-8")
    assert "Challenge this claim.\nIt is shallow." in segment
    assert segment.count('"sender":"m3taversal"') == 2
    assert "Forwarded Person" not in manifest["participants"]
    assert "fixture-secret-value" not in segment
    assert "TWITTERAPI_IO_KEY=[REDACTED_SECRET]" in segment
    assert manifest["secret_redaction"] == {"message_count": 1, "message_ids": ["102"], "value_count": 1}
    assert '"reply_to":"101"' in segment
    assert "https://example.com/source" in segment
    assert manifest["attachments"][0]["message_ids"] == ["102"]
    assert manifest["attachments"][0]["sha256"] == module.sha256_bytes(b"bounded evidence\n")
    assert str(export.parent) not in json.dumps(manifest)
    assert stat.S_IMODE(output.stat().st_mode) == 0o700
    assert all(stat.S_IMODE(path.stat().st_mode) == 0o600 for path in output.iterdir())


def test_prepare_is_deterministic_and_splits_only_at_message_boundaries(tmp_path: Path) -> None:
    module = load_module()
    export = write_export(tmp_path)

    first = module.prepare(export, tmp_path / "first", 1000)
    second = module.prepare(export, tmp_path / "second", 1000)

    assert first == second
    assert first["corpus_sha256"] == second["corpus_sha256"]
    assert all(row["characters"] <= 1000 for row in first["segments"])


def test_unsafe_attachment_path_fails_before_output_creation(tmp_path: Path) -> None:
    module = load_module()
    export = write_export(tmp_path, export_html("files/../../outside.txt"))
    output = tmp_path / "must-not-exist"

    with pytest.raises(module.ExportError, match="unsafe Telegram attachment path"):
        module.prepare(export, output, 1000)

    assert not output.exists()


def test_existing_output_directory_is_never_overwritten(tmp_path: Path) -> None:
    module = load_module()
    export = write_export(tmp_path)
    output = tmp_path / "existing"
    output.mkdir()
    marker = output / "keep"
    marker.write_text("unchanged", encoding="utf-8")

    with pytest.raises(module.ExportError, match="already exists"):
        module.prepare(export, output, 1000)

    assert marker.read_text(encoding="utf-8") == "unchanged"
