"""Visual-odometry hot-path dataclasses (ARCH-02).

Includes Features, Matches, RelativePose, Motion. `VOEstimate` is a
module-level alias of `RelativePose` per ARCH-02 canonical-name list —
the existing impl returns RelativePose; VOEstimate is the protocol-level
name.

eq=False on every dataclass that carries np.ndarray fields, matching
Pydantic's prior incomparability under arbitrary_types_allowed.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

import numpy as np


@dataclass(slots=True, frozen=True, eq=False)
class Features:
    """Extracted image features (e.g., from SuperPoint)."""

    keypoints: np.ndarray      # (N, 2)
    descriptors: np.ndarray    # (N, 256)
    scores: np.ndarray         # (N,)


@dataclass(slots=True, frozen=True, eq=False)
class Matches:
    """Matches between two sets of features (e.g., from LightGlue)."""

    matches: np.ndarray        # (M, 2)
    scores: np.ndarray         # (M,)
    keypoints1: np.ndarray     # (M, 2)
    keypoints2: np.ndarray     # (M, 2)


@dataclass(slots=True, frozen=True, eq=False)
class RelativePose:
    """Relative pose between two frames.

    Note: `covariance` is included as an optional 6×6 SE(3) uncertainty
    matrix. The legacy Pydantic model did not declare this field but
    silently accepted `covariance=...` kwargs (Pydantic v2 default
    `extra="ignore"` behavior). Several stage1 tests rely on that
    construction signature; declaring the field here preserves the
    contract under the dataclass migration without editing tests.
    """

    translation: np.ndarray    # (3,)
    rotation: np.ndarray       # (3, 3)
    confidence: float
    inlier_count: int
    total_matches: int
    tracking_good: bool
    scale_ambiguous: bool = True
    chunk_id: Optional[str] = None
    covariance: Optional[np.ndarray] = None  # (6, 6) SE(3) covariance — optional


@dataclass(slots=True, frozen=True, eq=False)
class Motion:
    """Motion estimate from OpenCV."""

    translation: np.ndarray    # (3,) unit vector
    rotation: np.ndarray       # (3, 3) rotation matrix
    inliers: np.ndarray        # Boolean mask of inliers
    inlier_count: int


# ARCH-02 canonical name — VOEstimate IS the relative pose returned by VIO.
VOEstimate = RelativePose
