"""Pandera schema contracts for open deviation bar DataFrames.

Defines BarOutputSchema — the data contract between the Rust processor's
Arrow output and ClickHouse storage. Catches column naming mismatches
and timestamp scale errors (μs vs ms) at test time.

Issue #166: Data Contract Validation initiative.
"""

from __future__ import annotations

import pandera.polars as pa

# Timestamp bounds in milliseconds:
#   Lower: 2001-09-09 (~1e12 ms) — anything below is likely seconds
#   Upper: 2049-03-22 (~2.5e12 ms) — anything above is likely microseconds
_TS_MS_MIN = 1_000_000_000_000
_TS_MS_MAX = 2_500_000_000_000


class BarOutputSchema(pa.DataFrameModel):
    """Schema contract for open deviation bar output DataFrames.

    Validates column presence, types, and value bounds. Designed to catch
    the three bugs from the v12.50.0-v12.50.2 hotfix cascade:

    1. Column naming: Arrow produces open_time/close_time, ClickHouse
       expects open_time_ms/close_time_ms — missing _ms suffix detected.
    2. Timestamp scale: Arrow returns μs (1e15), ClickHouse expects ms
       (1e12) — out-of-bounds values detected by ge/le constraints.
    3. Required columns: missing columns raise SchemaError immediately.
    """

    # --- Timestamps (milliseconds) ---
    open_time_ms: int = pa.Field(ge=_TS_MS_MIN, le=_TS_MS_MAX)
    close_time_ms: int = pa.Field(ge=_TS_MS_MIN, le=_TS_MS_MAX)

    # --- OHLCV ---
    open: float = pa.Field(gt=0)
    high: float = pa.Field(gt=0)
    low: float = pa.Field(gt=0)
    close: float = pa.Field(gt=0)
    volume: float = pa.Field(ge=0)

    # --- Trade IDs (Stathera invariant) ---
    first_agg_trade_id: int = pa.Field(ge=0)
    last_agg_trade_id: int = pa.Field(ge=0)

    class Config:
        strict = False  # Allow extra columns (microstructure, turnover, etc.)
        coerce = False  # Don't silently cast types
