#!/usr/bin/env python3
"""
File: tests/_fail_only_stream_plugin.py
Purpose: Provide fail-only NDJSON streaming plugin that merges per-worker files
         and writes session summary while respecting LLM_REPORT_DIR.
Context: Extracted from conftest.py to avoid duplication and enable explicit
         plugin loading via -p in subprocess calls (xdist scenarios).
"""

import json
import os
from pathlib import Path
from typing import Any


class FailOnlyNDJSONPlugin:
    """Plugin to stream failures to NDJSON and merge per-worker reports."""

    def __init__(self):
        self.report_dir = Path(os.getenv("LLM_REPORT_DIR", "reports"))
        # Create stream subdirectory for per-worker files
        self.stream_dir = self.report_dir / "stream"
        self.stream_dir.mkdir(parents=True, exist_ok=True)
        self.stream_fail_enabled = os.getenv("LLM_REPORT_STREAM_FAIL", "0") == "1"
        self.failures = []
        self.session_summary = {}
        self.worker_id = os.getenv("PYTEST_XDIST_WORKER", "master")

    def pytest_sessionstart(self, session: Any) -> None:
        """Initialize session tracking."""
        self.failures = []
        self.session_summary = {
            "session_id": "unknown",
            "total": 0,
            "passed": 0,
            "failed": 0,
            "errors": 0,
            "skipped": 0,
        }

    def pytest_runtest_logreport(self, report: Any) -> None:
        """Capture test results and stream failures."""
        if report.when == "call":
            if report.outcome == "failed":
                self.failures.append({
                    "event": "test_phase",
                    "nodeid": report.nodeid,
                    "outcome": "failed",
                })
                if self.stream_fail_enabled:
                    self._write_fail_ndjson(report.nodeid, "failed")
                self.session_summary["failed"] = self.session_summary.get("failed", 0) + 1
            elif report.outcome == "passed":
                self.session_summary["passed"] = self.session_summary.get("passed", 0) + 1
            elif report.outcome == "error":
                self.session_summary["errors"] = self.session_summary.get("errors", 0) + 1
            elif report.outcome == "skipped":
                self.session_summary["skipped"] = self.session_summary.get("skipped", 0) + 1

    def pytest_sessionfinish(self, session: Any, exitstatus: int) -> None:
        """Write merged NDJSON and summary at session end."""
        if self.stream_fail_enabled:
            # Merge per-worker NDJSON files (if xdist present)
            self._merge_worker_ndjson()
            # Write final summary
            self._write_summary_ndjson()

    def _write_fail_ndjson(self, nodeid: str, outcome: str) -> None:
        """Write individual failure to per-worker fail stream."""
        # For xdist workers, write to per-worker file; for master, write to main file
        if self.worker_id != "master":
            fail_file = self.stream_dir / f"llm_fail_{self.worker_id}.ndjson"
        else:
            fail_file = self.report_dir / "llm_fail.ndjson"

        entry = json.dumps({
            "event": "test_phase",
            "nodeid": nodeid,
            "outcome": outcome,
        }, ensure_ascii=False)
        with fail_file.open("a", encoding="utf-8") as f:
            f.write(entry + "\n")

    def _merge_worker_ndjson(self) -> None:
        """Merge per-worker NDJSON files into single fail file."""
        # Only merge on master process; workers skip this
        if self.worker_id != "master":
            return

        # Look for worker files in stream subdirectory
        worker_files = list(self.stream_dir.glob("llm_fail_worker*.ndjson"))
        if not worker_files:
            return

        merged_entries = []
        for worker_file in sorted(worker_files):
            with worker_file.open("r", encoding="utf-8") as f:
                for line in f:
                    if line.strip():
                        try:
                            merged_entries.append(json.loads(line))
                        except json.JSONDecodeError:
                            pass

        # Write merged file to main report dir
        merged_file = self.report_dir / "llm_fail.ndjson"
        with merged_file.open("w", encoding="utf-8") as f:
            for entry in merged_entries:
                f.write(json.dumps(entry, ensure_ascii=False) + "\n")

    def _write_summary_ndjson(self) -> None:
        """Write session summary. On master, append to merged llm_fail.ndjson."""
        if self.worker_id != "master":
            return

        # Append session summary to merged fail file
        fail_file = self.report_dir / "llm_fail.ndjson"
        summary_entry = dict(self.session_summary)
        summary_entry["event"] = "session_summary"
        with fail_file.open("a", encoding="utf-8") as f:
            f.write(json.dumps(summary_entry, ensure_ascii=False) + "\n")

        # Also write standalone summary file
        summary_file = self.report_dir / "llm_summary.ndjson"
        with summary_file.open("w", encoding="utf-8") as f:
            f.write(json.dumps(summary_entry, ensure_ascii=False) + "\n")


def pytest_configure(config: Any) -> None:
    """Register the plugin."""
    config.pluginmanager.register(FailOnlyNDJSONPlugin(), "fail_only_ndjson")
