"""Line zone counting and annotator.

Minimal implementation: track per-tracker last side of the line and detect crossings.
"""

import cv2
import numpy as np
from typing import Tuple, List, Optional
from ..core.detections import Detections


class LineZone:
    """A line detector that counts when tracked objects cross the line.

    The line is defined by two points (x1,y1)->(x2,y2). Use `update(detections)`
    each frame to register detections (requires `detections.tracker_id`).
    """

    def __init__(self, p1: Tuple[int, int], p2: Tuple[int, int], name: Optional[str] = None):
        self.p1 = np.array(p1, dtype=float)
        self.p2 = np.array(p2, dtype=float)
        self.name = name or f"LineZone_{id(self)}"

        # analytics
        self.total_count = 0
        self.directional_count = {"pos->neg": 0, "neg->pos": 0}
        # last seen side per tracker id
        self._last_side = {}

    def _side(self, point: Tuple[float, float]) -> int:
        # sign of cross product from line to point
        px, py = float(point[0]), float(point[1])
        x1, y1 = self.p1
        x2, y2 = self.p2
        return int(np.sign((x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)))

    def update(self, detections: Detections) -> List[int]:
        """Process detections, update counts, and return list of tracker_ids that crossed."""
        crossed = []
        if len(detections) == 0 or detections.tracker_id is None:
            return crossed

        centers = detections.center
        for i in range(len(detections)):
            tid = int(detections.tracker_id[i])
            c = centers[i]
            side = self._side(c)
            last = self._last_side.get(tid, None)
            if last is None:
                # first observation
                self._last_side[tid] = side
                continue

            if side == 0 or last == 0:
                # touching line: treat as no-cross
                self._last_side[tid] = side
                continue

            if side != last:
                # crossed
                crossed.append(tid)
                if last > 0 and side < 0:
                    self.directional_count["pos->neg"] += 1
                elif last < 0 and side > 0:
                    self.directional_count["neg->pos"] += 1
                self.total_count += 1
                self._last_side[tid] = side

        return crossed

    def reset(self):
        self.total_count = 0
        self.directional_count = {"pos->neg": 0, "neg->pos": 0}
        self._last_side.clear()


class LineZoneAnnotator:
    """Annotator that draws a line and simple counters on the frame."""

    def __init__(self, color=(0, 255, 0), thickness: int = 2, font_scale: float = 0.6, padding: int = 6):
        self.color = color
        self.thickness = thickness
        self.font_scale = font_scale
        self.padding = padding

    def annotate(self, image: np.ndarray, line: LineZone) -> np.ndarray:
        annotated = image.copy()
        x1, y1 = int(line.p1[0]), int(line.p1[1])
        x2, y2 = int(line.p2[0]), int(line.p2[1])

        cv2.line(annotated, (x1, y1), (x2, y2), self.color, self.thickness)

        # draw arrow to indicate primary direction (p1->p2)
        vx, vy = x2 - x1, y2 - y1
        norm = max(1.0, (vx * vx + vy * vy) ** 0.5)
        ux, uy = int(x1 + 0.8 * vx), int(y1 + 0.8 * vy)
        cv2.arrowedLine(annotated, (ux, uy), (x2, y2), self.color, max(1, self.thickness - 1), tipLength=0.2)

        # draw counts
        text = f"Total: {line.total_count}  +:{line.directional_count.get('neg->pos',0)} -:{line.directional_count.get('pos->neg',0)}"
        (w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, self.font_scale, 1)
        x_text = min(max(10, x1), annotated.shape[1] - w - 10)
        y_text = min(max(10, y1 - 10), annotated.shape[0] - 10)
        cv2.rectangle(annotated, (x_text - self.padding, y_text - h - self.padding), (x_text + w + self.padding, y_text + self.padding), (0, 0, 0), -1)
        cv2.putText(annotated, text, (x_text, y_text), cv2.FONT_HERSHEY_SIMPLEX, self.font_scale, self.color, 1)

        return annotated
