#!/usr/bin/env python3
"""
ShotTrack OCR Accuracy Benchmark — Phase 10
Reads pre-fetched sample data from stdin (JSON), runs OCR on each photo,
compares to ground truth, and outputs an accuracy report JSON to stdout.

Input (stdin) JSON format:
{
  "samples": [
    {
      "sampleId": "cxxx",
      "photoPath": "/abs/path/to/photo.jpg",
      "groundTruth": {"counter": 12345, "date": "25/06/24", "time": "14:30:00", "cycle_time": "12.5"}
    }
  ],
  "regions": [
    {"fieldName": "counter", "x": 0.1, "y": 0.2, "w": 0.3, "h": 0.1, "ocrEngine": "easyocr", ...}
  ]
}

Output JSON:
{
  "totalSamples": N,
  "perField": {"counter": {"correct": N, "total": N, "accuracy": 0.95}, ...},
  "samples": [{"sampleId": "...", "groundTruth": {...}, "ocrResult": {...}, "matches": {...}}]
}
"""
import sys
import json
import re

try:
    import easyocr
    import cv2
    import numpy as np
except ImportError as e:
    print(json.dumps({"error": f"Missing dependency: {e}"}), flush=True)
    sys.exit(1)

# Import OCR functions from sibling module
import importlib.util, os
_spec = importlib.util.spec_from_file_location(
    "ocr_engine",
    os.path.join(os.path.dirname(__file__), "ocr_engine.py"),
)
_mod = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_mod)
preprocess = _mod.preprocess
run_region_ocr = _mod.run_region_ocr
extract_counter = _mod.extract_counter
extract_date = _mod.extract_date
extract_time = _mod.extract_time
boxes_to_list = _mod.boxes_to_list


def field_matches(field: str, got, expected) -> bool:
    """Compare OCR result to ground truth for a named field."""
    if expected is None or expected == "":
        return True  # no ground truth → skip
    if got is None:
        return False
    got_s = str(got).strip()
    exp_s = str(expected).strip()
    if field == "counter":
        try:
            return int(re.sub(r"[^0-9]", "", got_s)) == int(re.sub(r"[^0-9]", "", exp_s))
        except ValueError:
            return False
    elif field in ("date", "time"):
        # Match first 5 chars (HH:MM for time, DD/MM for date)
        return got_s[:5] == exp_s[:5]
    elif field == "cycle_time":
        try:
            return abs(float(got_s) - float(exp_s)) < 1.0
        except ValueError:
            return False
    else:
        return got_s.lower() == exp_s.lower()


def run_benchmark(samples, regions):
    reader = easyocr.Reader(["en"], gpu=False, verbose=False)

    field_stats: dict = {}
    sample_results = []

    for s in samples:
        photo_path = s["photoPath"]
        ground_truth = s.get("groundTruth") or {}
        sample_id = s["sampleId"]

        img = cv2.imread(photo_path)
        if img is None:
            sample_results.append({
                "sampleId": sample_id,
                "error": f"Cannot read image: {photo_path}",
            })
            continue

        h, w = img.shape[:2]
        if w > 1600:
            img = cv2.resize(img, (1600, int(h * 1600 / w)))

        if regions:
            ocr_out = run_region_ocr(img, reader, regions)
            # Map region results to flat field dict
            ocr_fields = {
                "counter": ocr_out.get("counter", {}).get("value"),
                "date":    ocr_out.get("date",    {}).get("value"),
                "time":    ocr_out.get("time",    {}).get("value"),
                "cycle_time": ocr_out.get("cycle_time", {}).get("value"),
            }
        else:
            # Full-image fallback
            from collections import Counter as Ctr
            variants = ["original", "inverted", "clahe"]
            all_counts = []
            orig_tuples = []
            for variant in variants:
                processed = preprocess(img, variant)
                raw = reader.readtext(processed)
                box_list = boxes_to_list(raw)
                tuples = [(b["bbox"], b["text"], b["conf"]) for b in box_list]
                if variant == "original":
                    orig_tuples = tuples
                cval, cconf = extract_counter(tuples)
                if cval is not None:
                    all_counts.append((cval, cconf))
            counter = Ctr(r[0] for r in all_counts).most_common(1)[0][0] if all_counts else None
            ocr_fields = {
                "counter": counter,
                "date": extract_date(orig_tuples),
                "time": extract_time(orig_tuples),
                "cycle_time": None,
            }

        matches = {}
        for field, expected in ground_truth.items():
            if expected in (None, ""):
                continue
            got = ocr_fields.get(field)
            match = field_matches(field, got, expected)
            matches[field] = match
            if field not in field_stats:
                field_stats[field] = {"correct": 0, "total": 0}
            field_stats[field]["total"] += 1
            if match:
                field_stats[field]["correct"] += 1

        sample_results.append({
            "sampleId": sample_id,
            "groundTruth": ground_truth,
            "ocrResult": ocr_fields,
            "matches": matches,
        })

    per_field = {}
    for field, stats in field_stats.items():
        per_field[field] = {
            **stats,
            "accuracy": round(stats["correct"] / stats["total"], 4) if stats["total"] else 0.0,
        }

    return {
        "totalSamples": len(samples),
        "perField": per_field,
        "samples": sample_results,
    }


if __name__ == "__main__":
    try:
        payload = json.loads(sys.stdin.read())
    except json.JSONDecodeError as e:
        print(json.dumps({"error": f"Invalid input JSON: {e}"}), flush=True)
        sys.exit(1)

    samples = payload.get("samples", [])
    regions = payload.get("regions", [])

    if not samples:
        print(json.dumps({"error": "No samples provided"}), flush=True)
        sys.exit(1)

    result = run_benchmark(samples, regions)
    print(json.dumps(result), flush=True)
