from __future__ import annotations

import contextlib
import hashlib
import io
import json
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from src.cli import main


SAMPLE_AUDIT_RECORD = {
    "photo_sha256": "x" * 64,
    "source_path": r"C:\inbox\a.jpg",
    "source_relpath": "a.jpg",
    "deal_id": None,
    "confidence": None,
    "match_reason": "match_disabled",
    "website_webp_path": r"C:\out\website\a.webp",
    "website_webp_exists": False,
    "website_webp_bytes": None,
    "gbp_jpg_path": r"C:\out\gbp\a.jpg",
    "gbp_jpg_exists": False,
    "gbp_jpg_bytes": None,
    "gbp_png_path": r"C:\out\gbp\a.png",
    "gbp_png_exists": False,
    "gbp_png_bytes": None,
}


class TestReporting(unittest.TestCase):
    def test_run_writes_audit_artifacts(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            inbox = Path(tmp) / "inbox"
            out = Path(tmp) / "out"
            inbox.mkdir(parents=True, exist_ok=True)
            photo = inbox / "a.jpg"
            content = b"hello"
            photo.write_bytes(content)
            expected_sha = hashlib.sha256(content).hexdigest()

            stdout = io.StringIO()
            with (
                patch.dict(os.environ, {"INBOX_PATH": str(inbox), "OUTPUT_ROOT": str(out)}, clear=True),
                contextlib.redirect_stdout(stdout),
            ):
                code = main(["run", "--run-id", "RUN1", "--no-match"])

            self.assertEqual(code, 0)
            audit_dir = out / "audit" / "RUN1"
            self.assertTrue((audit_dir / "audit.jsonl").exists())
            self.assertTrue((audit_dir / "summary.json").exists())

            rec = json.loads((audit_dir / "audit.jsonl").read_text(encoding="utf-8").splitlines()[0])
            self.assertEqual(rec["photo_sha256"], expected_sha)
            self.assertEqual(rec["source_relpath"], "a.jpg")
            self.assertEqual(rec["website_webp_path"], str(out / "website" / "a.webp"))
            self.assertEqual(rec["gbp_jpg_path"], str(out / "gbp" / "a.jpg"))
            self.assertEqual(rec["gbp_png_path"], str(out / "gbp" / "a.png"))

    def test_run_dry_run_does_not_write_audit_artifacts(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            inbox = Path(tmp) / "inbox"
            out = Path(tmp) / "out"
            inbox.mkdir(parents=True, exist_ok=True)
            (inbox / "a.jpg").write_bytes(b"hello")

            stdout = io.StringIO()
            with (
                patch.dict(os.environ, {"INBOX_PATH": str(inbox), "OUTPUT_ROOT": str(out)}, clear=True),
                contextlib.redirect_stdout(stdout),
            ):
                code = main(["run", "--run-id", "RUN1", "--no-match", "--dry-run"])

            self.assertEqual(code, 0)
            self.assertFalse((out / "audit" / "RUN1").exists())

    def test_report_dry_run_prints_json_with_required_fields(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            out = Path(tmp) / "out"
            audit_dir = out / "audit" / "RUN1"
            audit_dir.mkdir(parents=True, exist_ok=True)
            (audit_dir / "audit.jsonl").write_text(
                json.dumps(SAMPLE_AUDIT_RECORD, sort_keys=True, ensure_ascii=True)
                + "\n",
                encoding="utf-8",
                newline="\n",
            )

            stdout = io.StringIO()
            with (
                patch.dict(os.environ, {"OUTPUT_ROOT": str(out)}, clear=True),
                contextlib.redirect_stdout(stdout),
            ):
                code = main(["report", "--dry-run"])

            self.assertEqual(code, 0)
            payload = json.loads(stdout.getvalue())
            self.assertIsInstance(payload, list)
            self.assertEqual(payload[0]["photo_sha256"], "x" * 64)
            self.assertIn("deal_id", payload[0])
            self.assertIn("confidence", payload[0])
            self.assertIn("website_webp_path", payload[0])
            self.assertIn("gbp_jpg_path", payload[0])
            self.assertIn("gbp_png_path", payload[0])

    def test_report_run_writes_csv_and_json(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            out = Path(tmp) / "out"
            audit_dir = out / "audit" / "RUN1"
            audit_dir.mkdir(parents=True, exist_ok=True)
            (audit_dir / "audit.jsonl").write_text("{}\n", encoding="utf-8", newline="\n")

            with patch.dict(os.environ, {"OUTPUT_ROOT": str(out)}, clear=True):
                code = main(["report", "--run-id", "RUN1"])

            self.assertEqual(code, 0)
            self.assertTrue((audit_dir / "audit.csv").exists())
            self.assertTrue((audit_dir / "audit.json").exists())
