from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from rights_filter.analysis.preprocessing import ImagePayload

SUPPORTED_IMAGE_SUFFIXES = {
    ".avif",
    ".bmp",
    ".gif",
    ".jpeg",
    ".jfif",
    ".jpg",
    ".pjp",
    ".pjpeg",
    ".png",
    ".svg",
    ".webp",
}


class LocalSubmissionImageStore:
    def __init__(self, root: Path | str, public_prefix: str = "/media") -> None:
        self.root = Path(root).resolve()
        self.public_prefix = public_prefix.rstrip("/")

    def submission_records(self) -> list[dict[str, Any]]:
        manifest = self.root / "submissions.json"
        if manifest.exists():
            records = json.loads(manifest.read_text(encoding="utf-8"))
            normalized = [
                self._normalize_manifest_record(record)
                for record in records
                if isinstance(record, dict)
                and record.get("file")
                and self._safe_path(str(record["file"])).exists()
            ]
            return self._with_unlisted_files(normalized)
        return self._scan_records()

    def image_payload(self, submission_id: str) -> ImagePayload:
        record = self._record_for(submission_id)
        path = self._safe_path(record["file"])
        return ImagePayload(
            content=path.read_bytes(),
            width=int(record["width"]),
            height=int(record["height"]),
            metadata={
                "submission_id": submission_id,
                "title": str(record.get("title", submission_id)),
                "file": str(record["file"]),
                "format": str(record.get("format", "")),
            },
        )

    def media_path(self, relative_path: str) -> Path:
        return self._safe_path(relative_path)

    def _record_for(self, submission_id: str) -> dict[str, Any]:
        for record in self.submission_records():
            if record["id"] == submission_id:
                return record
        raise KeyError(submission_id)

    def _normalize_manifest_record(self, record: dict[str, Any]) -> dict[str, Any]:
        path = self._safe_path(str(record["file"]))
        normalized = dict(record)
        normalized["file"] = str(Path(str(record["file"])).as_posix())
        normalized.setdefault("id", path.stem)
        normalized.setdefault("title", normalized["id"])
        normalized.setdefault("width", 1)
        normalized.setdefault("height", 1)
        normalized.setdefault("submitted_at", "")
        normalized["asset"] = f"{self.public_prefix}/{normalized['file']}"
        normalized["format"] = path.suffix.lstrip(".").upper() or "FILE"
        return normalized

    def _scan_records(self) -> list[dict[str, Any]]:
        if not self.root.exists():
            return []
        records: list[dict[str, Any]] = []
        for path in sorted(self.root.rglob("*")):
            if path.name == "submissions.json" or not path.is_file():
                continue
            if path.suffix.lower() not in SUPPORTED_IMAGE_SUFFIXES:
                continue
            relative = path.relative_to(self.root).as_posix()
            width, height = _image_size(path)
            records.append(
                {
                    "id": path.stem,
                    "title": path.stem,
                    "file": relative,
                    "asset": f"{self.public_prefix}/{relative}",
                    "width": width,
                    "height": height,
                    "submitted_at": "",
                    "format": path.suffix.lstrip(".").upper(),
                }
            )
        return records

    def _with_unlisted_files(self, records: list[dict[str, Any]]) -> list[dict[str, Any]]:
        seen_ids = {str(record["id"]) for record in records}
        seen_files = {str(record["file"]) for record in records}
        merged = list(records)
        for record in self._scan_records():
            if record["id"] in seen_ids or record["file"] in seen_files:
                continue
            merged.append(record)
        return merged

    def _safe_path(self, relative_path: str) -> Path:
        path = (self.root / relative_path).resolve()
        if path != self.root and self.root not in path.parents:
            raise ValueError("manifest path points outside image store")
        return path


def _image_size(path: Path) -> tuple[int, int]:
    try:
        from PIL import Image

        with Image.open(path) as image:
            return int(image.width), int(image.height)
    except Exception:
        return 1, 1
