from __future__ import annotations

import json
from pathlib import Path
import tempfile
import unittest

from .services import ConcordanceDraftStore, ConcordanceEditorPaths


def sample_settings() -> list[dict]:
    return [
        {
            "title": "Alpha",
            "composer": "John Dowland",
            "orig_comp": "",
            "source": "Source A",
            "document": "Doc A",
            "page": "p.1",
            "concordances": "piece-a",
            "Sections": [{"parts": [{"midi": "/midi/a.mid", "PDF": "/pdf/a.pdf"}]}],
        },
        {
            "title": "Beta",
            "composer": "John Dowland",
            "orig_comp": "",
            "source": "Source B",
            "document": "Doc B",
            "page": "p.2",
            "concordances": "piece-a",
            "Sections": [{"parts": [{"midi": "/midi/b.mid", "PDF": "/pdf/b.pdf"}]}],
        },
        {
            "title": "Gamma",
            "composer": "Francis Cutting",
            "orig_comp": "",
            "source": "Source C",
            "document": "Doc C",
            "page": "p.3",
            "concordances": "piece-c",
            "Sections": [{"parts": [{"midi": "/midi/c.mid", "PDF": "/pdf/c.pdf"}]}],
        },
    ]


class ConcordanceDraftStoreTests(unittest.TestCase):
    def setUp(self) -> None:
        self.tempdir = tempfile.TemporaryDirectory()
        self.root = Path(self.tempdir.name)
        self.live_root = self.root / "live"
        self.draft_root = self.root / "draft"
        self.live_root.mkdir(parents=True, exist_ok=True)
        (self.live_root / "settings.json").write_text(
            json.dumps(sample_settings(), indent=2) + "\n",
            encoding="utf-8",
        )
        self.store = ConcordanceDraftStore(
            ConcordanceEditorPaths.from_roots(self.live_root, self.draft_root)
        )

    def tearDown(self) -> None:
        self.tempdir.cleanup()

    def test_make_singleton_blanks_selected_row_only(self) -> None:
        session = self.store.start_session("tester", discard=True)
        self.assertTrue(session["draft_exists"])

        result = self.store.apply_operation(
            left_setting_key="Source B || Doc B || p.2",
            operation="make_singleton",
            user_name="tester",
        )
        self.assertTrue(result["ok"])

        draft = self.store.draft_settings()
        by_key = {row["source"]: row.get("concordances", "") for row in draft}
        self.assertEqual(by_key["Source A"], "piece-a")
        self.assertEqual(by_key["Source B"], "")
        self.assertEqual(by_key["Source C"], "piece-c")

        detail = self.store.setting_detail("Source B || Doc B || p.2")
        self.assertEqual(detail["concordance_count"], 1)
        self.assertTrue(detail["is_provisional_singleton"])

    def test_merge_group_into_draft_singleton_generates_piece_key(self) -> None:
        self.store.start_session("tester", discard=True)
        self.store.apply_operation(
            left_setting_key="Source C || Doc C || p.3",
            operation="make_singleton",
            user_name="tester",
        )

        self.store.apply_operation(
            left_setting_key="Source A || Doc A || p.1",
            right_setting_key="Source C || Doc C || p.3",
            operation="merge_setting_groups",
            user_name="tester",
        )

        draft = self.store.draft_settings()
        by_source = {row["source"]: row.get("concordances", "") for row in draft}
        self.assertTrue(by_source["Source A"].startswith("manual-"))
        self.assertEqual(by_source["Source A"], by_source["Source B"])
        self.assertEqual(by_source["Source A"], by_source["Source C"])

    def test_prepare_export_writes_manifest_hashes(self) -> None:
        self.store.start_session("tester", discard=True)
        export = self.store.prepare_export("tester")
        self.assertTrue(export["ok"])

        manifest = json.loads(self.store.paths.draft_manifest_path.read_text(encoding="utf-8"))
        self.assertEqual(
            manifest["base_settings_sha256"],
            export["session"]["base_settings_sha256"],
        )
        self.assertEqual(
            manifest["settings_new_sha256"],
            export["session"]["draft_settings_sha256"],
        )

    def test_stale_draft_is_detected(self) -> None:
        self.store.start_session("tester", discard=True)
        live_payload = sample_settings()
        live_payload[0]["concordances"] = "piece-z"
        (self.live_root / "settings.json").write_text(
            json.dumps(live_payload, indent=2) + "\n",
            encoding="utf-8",
        )

        session = self.store.session_status()
        self.assertTrue(session["stale"])


if __name__ == "__main__":
    unittest.main()
