"""
Provenance sidecar writer for waterfall enrichment.

Writes JSONL (JSON Lines) files with per-row enrichment events for audit trails.
"""

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import List, Optional

from .models import ProvenanceEvent, WaterfallSummary


class ProvenanceWriter:
    """Write provenance events to JSONL sidecar file.

    Events are buffered in memory and written to disk periodically or on close.
    The sidecar file is named <output_csv>.provenance.jsonl
    """

    def __init__(
        self,
        output_path: Path,
        buffer_size: int = 100,
        enabled: bool = True,
    ):
        """Initialize provenance writer.

        Args:
            output_path: Path to the output CSV file (sidecar will be .provenance.jsonl)
            buffer_size: Number of events to buffer before flushing
            enabled: If False, no provenance is written
        """
        self.output_path = Path(output_path)
        self.sidecar_path = self.output_path.with_suffix(
            self.output_path.suffix + ".provenance.jsonl"
        )
        self.buffer_size = buffer_size
        self.enabled = enabled

        self._buffer: List[ProvenanceEvent] = []
        self._run_id: Optional[str] = None
        self._start_time: Optional[datetime] = None
        self._events_written: int = 0
        self._file_handle = None

    def start_run(self, run_id: str, config_name: str) -> None:
        """Start a new provenance run, writing header.

        Args:
            run_id: Unique identifier for this run
            config_name: Name of the waterfall configuration
        """
        if not self.enabled:
            return

        self._run_id = run_id
        self._start_time = datetime.now(timezone.utc)

        # Write header event
        header = {
            "event": "run_start",
            "run_id": run_id,
            "config_name": config_name,
            "start_time": self._start_time.isoformat() + "Z",
            "version": "1",
        }

        self._open_file()
        self._write_line(header)

    def add_event(self, event: ProvenanceEvent) -> None:
        """Add an enrichment event to the buffer.

        Args:
            event: Provenance event to record
        """
        if not self.enabled:
            return

        self._buffer.append(event)

        if len(self._buffer) >= self.buffer_size:
            self.flush()

    def flush(self) -> None:
        """Flush buffered events to disk."""
        if not self.enabled or not self._buffer:
            return

        if self._file_handle is None:
            self._open_file()

        for event in self._buffer:
            event_dict = event.to_dict()
            event_dict["event"] = "enrichment"
            event_dict["run_id"] = self._run_id
            self._write_line(event_dict)
            self._events_written += 1

        self._buffer.clear()

    def finish_run(self, summary: WaterfallSummary) -> None:
        """Finish the run, writing summary and closing file.

        Args:
            summary: Final waterfall summary
        """
        if not self.enabled:
            return

        self.flush()

        # Write summary event
        end_time = datetime.now(timezone.utc)
        footer = {
            "event": "run_end",
            "run_id": self._run_id,
            "end_time": end_time.isoformat() + "Z",
            "duration_seconds": (
                (end_time - self._start_time).total_seconds()
                if self._start_time
                else 0
            ),
            "events_written": self._events_written,
            "summary": {
                "rows_total": summary.rows_total,
                "rows_enriched": summary.rows_enriched,
                "rows_complete": summary.rows_complete,
                "rows_failed": summary.rows_failed,
                "fields_filled": summary.fields_filled,
                "provider_stats": summary.provider_stats,
                "cache_stats": summary.cache_stats,
                "estimated_cost_usd": summary.estimated_cost_usd,
            },
        }
        self._write_line(footer)

        self._close_file()

    def _open_file(self) -> None:
        """Open sidecar file for writing."""
        if self._file_handle is not None:
            return

        # Ensure parent directory exists
        self.sidecar_path.parent.mkdir(parents=True, exist_ok=True)

        self._file_handle = self.sidecar_path.open("w", encoding="utf-8")

    def _write_line(self, data: dict) -> None:
        """Write a single JSON line."""
        if self._file_handle is None:
            self._open_file()

        self._file_handle.write(json.dumps(data, default=str) + "\n")

    def _close_file(self) -> None:
        """Close sidecar file."""
        if self._file_handle is not None:
            self._file_handle.close()
            self._file_handle = None

    def __enter__(self) -> "ProvenanceWriter":
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        self.flush()
        self._close_file()

    @property
    def sidecar_file(self) -> Path:
        """Return path to sidecar file."""
        return self.sidecar_path


def read_provenance(sidecar_path: Path) -> List[dict]:
    """Read provenance events from sidecar file.

    Args:
        sidecar_path: Path to .provenance.jsonl file

    Returns:
        List of event dictionaries
    """
    events = []
    with sidecar_path.open("r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line:
                events.append(json.loads(line))
    return events
