from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import cv2
import numpy as np


@dataclass
class Template:
    name: str
    image: np.ndarray
    threshold: float = 0.85


@dataclass
class CacheResult:
    confidence: float
    state: Any | None


class PerceptionCache:
    def __init__(self, templates_path: Path | str = "data/templates.json"):
        self.templates_path = Path(templates_path)
        self.templates: list[Template] = []
        self.load()

    def match(self, screenshot: np.ndarray) -> CacheResult:
        """
        Matches templates against the screenshot.
        Returns the confidence and the name of the best matching template.
        """
        best_match_confidence = 0.0
        best_match_name = None

        for template in self.templates:
            res = cv2.matchTemplate(screenshot, template.image, cv2.TM_CCOEFF_NORMED)
            _, max_val, _, _ = cv2.minMaxLoc(res)
            if max_val > best_match_confidence:
                best_match_confidence = max_val
                best_match_name = template.name

        if best_match_confidence > 0.85:  # TODO: Make this configurable per template
            return CacheResult(
                confidence=best_match_confidence, state={"template_name": best_match_name}
            )
        else:
            return CacheResult(confidence=best_match_confidence, state=None)

    def add(self, templates: list[Template]):
        self.templates.extend(templates)

    def persist(self):
        self.templates_path.parent.mkdir(parents=True, exist_ok=True)
        # Note: This is a simplified persistence mechanism.
        # A more robust solution would store templates as images and metadata in JSON.
        with self.templates_path.open("w") as f:
            json.dump(
                [{"name": t.name, "threshold": t.threshold} for t in self.templates], f, indent=2
            )

    def load(self):
        if self.templates_path.exists():
            with self.templates_path.open("r") as f:
                templates_data = json.load(f)
                # This is a simplified loading mechanism and assumes template images are stored elsewhere.
                # For now, we are not loading the actual images.
                self.templates = [
                    Template(name=t["name"], image=np.array([]), threshold=t["threshold"])
                    for t in templates_data
                ]


def crystallize_perception(screenshot: np.ndarray, vlm_response: Any) -> list[Template]:
    """
    Extracts reusable patterns from VLM output and generates OpenCV templates.
    This is a placeholder and needs to be implemented based on the actual VLM response format.
    """
    # Example implementation:
    # templates = []
    # for item in vlm_response.get("items", []):
    #     bbox = item.get("bounding_box")
    #     template_name = item.get("name")
    #     if bbox and template_name:
    #         x1, y1, x2, y2 = bbox
    #         template_image = screenshot[y1:y2, x1:x2]
    #         templates.append(Template(name=template_name, image=template_image))
    # return templates
    return []
