#!/usr/bin/env python3
"""Self-test the dependency-free battle effect pixel comparator.

The real goal still needs original runtime captures.  This test only proves the
comparison machinery can detect pass/diff/frame-count/missing-input cases when
valid PNG inputs are supplied.
"""

from __future__ import annotations

import json
import tempfile
from pathlib import Path
from typing import Any

import compare_battle_effect_pixel_oracle as compare


def rgba(width: int, height: int, color: tuple[int, int, int, int], changed: tuple[int, tuple[int, int, int, int]] | None = None) -> bytes:
    pixels = bytearray(color * (width * height))
    if changed is not None:
        index, next_color = changed
        start = index * 4
        pixels[start:start + 4] = bytes(next_color)
    return bytes(pixels)


def write_frame(path: Path, color: tuple[int, int, int, int], changed: tuple[int, tuple[int, int, int, int]] | None = None, size: tuple[int, int] = (4, 3)) -> None:
    width, height = size
    compare.encode_png_rgba(path, width, height, rgba(width, height, color, changed=changed))


def make_dir(path: Path) -> Path:
    path.mkdir(parents=True, exist_ok=True)
    return path


def assert_status(row: dict[str, Any], expected: str) -> None:
    actual = row.get("status")
    if actual != expected:
        raise AssertionError(f"expected status {expected!r}, got {actual!r}: {row!r}")


def run_selftest() -> dict[str, Any]:
    with tempfile.TemporaryDirectory(prefix="hwanse-pixel-compare-") as tmp_name:
        tmp = Path(tmp_name)
        compare.CAPTURES = tmp / "generated-diff"

        pass_original = make_dir(tmp / "pass" / "original")
        pass_web = make_dir(tmp / "pass" / "web")
        for index in range(2):
            write_frame(pass_original / f"frame_{index:05d}.png", (12, 34, 56, 255))
            write_frame(pass_web / f"frame_{index:05d}.png", (12, 34, 56, 255))
        pass_row = compare.compare_entry({
            "id": "selftest:pass",
            "originalCapture": str(pass_original),
            "webReferenceCapture": str(pass_web),
        })
        assert_status(pass_row, "pass")
        if pass_row.get("failedFrameCount") != 0:
            raise AssertionError(f"pass row should not report failed frames: {pass_row!r}")

        diff_original = make_dir(tmp / "diff" / "original")
        diff_web = make_dir(tmp / "diff" / "web")
        write_frame(diff_original / "frame_00000.png", (10, 20, 30, 255))
        write_frame(diff_web / "frame_00000.png", (10, 20, 30, 255), changed=(5, (250, 10, 10, 255)))
        diff_row = compare.compare_entry({
            "id": "selftest:diff",
            "originalCapture": str(diff_original),
            "webReferenceCapture": str(diff_web),
        })
        assert_status(diff_row, "differs")
        first_diff = (diff_row.get("frameComparisons") or [{}])[0]
        diff_path = first_diff.get("diff")
        if not diff_path or not (compare.ROOT / diff_path).exists():
            raise AssertionError(f"diff row did not create a diff PNG: {diff_row!r}")
        if int(first_diff.get("differingPixels") or 0) <= 0:
            raise AssertionError(f"diff row did not report differing pixels: {diff_row!r}")

        mismatch_original = make_dir(tmp / "mismatch" / "original")
        mismatch_web = make_dir(tmp / "mismatch" / "web")
        write_frame(mismatch_original / "frame_00000.png", (1, 2, 3, 255))
        write_frame(mismatch_original / "frame_00001.png", (1, 2, 3, 255))
        write_frame(mismatch_web / "frame_00000.png", (1, 2, 3, 255))
        mismatch_row = compare.compare_entry({
            "id": "selftest:mismatch",
            "originalCapture": str(mismatch_original),
            "webReferenceCapture": str(mismatch_web),
        })
        assert_status(mismatch_row, "frame-count-mismatch")

        missing_original_row = compare.compare_entry({
            "id": "selftest:missing-original",
            "originalCapture": str(tmp / "missing"),
            "webReferenceCapture": str(pass_web),
        })
        assert_status(missing_original_row, "missing-original-capture")

        missing_web_row = compare.compare_entry({
            "id": "selftest:missing-web",
            "originalCapture": str(pass_original),
            "webReferenceCapture": str(tmp / "missing"),
        })
        assert_status(missing_web_row, "missing-web-reference-capture")

        dimension_original = make_dir(tmp / "dimension" / "original")
        dimension_web = make_dir(tmp / "dimension" / "web")
        write_frame(dimension_original / "frame_00000.png", (1, 1, 1, 255), size=(4, 3))
        write_frame(dimension_web / "frame_00000.png", (1, 1, 1, 255), size=(3, 3))
        dimension_row = compare.compare_entry({
            "id": "selftest:dimension",
            "originalCapture": str(dimension_original),
            "webReferenceCapture": str(dimension_web),
        })
        assert_status(dimension_row, "differs")
        if (dimension_row.get("frameComparisons") or [{}])[0].get("status") != "dimension-mismatch":
            raise AssertionError(f"dimension mismatch not reported at frame level: {dimension_row!r}")

        return {
            "status": "ok",
            "checks": [
                {"id": "pass", "status": pass_row["status"]},
                {"id": "diff", "status": diff_row["status"], "diffPngCreated": True},
                {"id": "frame-count-mismatch", "status": mismatch_row["status"]},
                {"id": "missing-original", "status": missing_original_row["status"]},
                {"id": "missing-web", "status": missing_web_row["status"]},
                {"id": "dimension-mismatch", "status": dimension_row["status"]},
            ],
        }


def main() -> int:
    report = run_selftest()
    print(json.dumps(report, ensure_ascii=False, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
