"""Verifier interfaces and implementations."""

from __future__ import annotations

import os
import subprocess
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Protocol


@dataclass(slots=True)
class VerificationResult:
    passed: bool
    name: str
    summary: str
    evidence: dict[str, str | int | float | bool] = field(default_factory=dict)

    def to_dict(self) -> dict[str, object]:
        return {
            "passed": self.passed,
            "name": self.name,
            "summary": self.summary,
            "evidence": self.evidence,
        }


class Verifier(Protocol):
    name: str

    def verify(self) -> VerificationResult:
        ...


class StringContainsVerifier:
    name = "string_contains"

    def __init__(self, text: str, expected: str) -> None:
        self.text = text
        self.expected = expected

    def verify(self) -> VerificationResult:
        passed = self.expected in self.text
        return VerificationResult(
            passed=passed,
            name=self.name,
            summary="expected substring found" if passed else "expected substring not found",
            evidence={"expected": self.expected, "text_length": len(self.text)},
        )


class CommandVerifier:
    name = "command"

    def __init__(self, command: list[str], cwd: str | Path | None = None, timeout_seconds: float = 120.0) -> None:
        if not command:
            raise ValueError("command must not be empty")
        self.command = command
        self.cwd = Path(cwd) if cwd is not None else None
        self.timeout_seconds = timeout_seconds

    def verify(self) -> VerificationResult:
        executable_command = [sys.executable, *self.command[1:]] if self.command and self.command[0] == "python" else list(self.command)
        env = {
            **os.environ,
            "PYTHONDONTWRITEBYTECODE": "1",
            "PYTEST_DISABLE_PLUGIN_AUTOLOAD": "1",
            "PYTEST_ADDOPTS": "",
            "PYTHONHASHSEED": "0",
        }
        env.pop("PYTEST_CURRENT_TEST", None)
        try:
            completed = subprocess.run(
                executable_command,
                cwd=self.cwd,
                timeout=self.timeout_seconds,
                text=True,
                capture_output=True,
                check=False,
                env=env,
            )
        except subprocess.TimeoutExpired as exc:
            return VerificationResult(
                passed=False,
                name=self.name,
                summary=f"command timed out after {self.timeout_seconds} seconds",
                evidence={"timeout_seconds": self.timeout_seconds, "command": " ".join(self.command)},
            )
        except OSError as exc:
            return VerificationResult(
                passed=False,
                name=self.name,
                summary=f"command failed to start: {exc}",
                evidence={"command": " ".join(self.command)},
            )

        stdout_tail = completed.stdout[-500:]
        stderr_tail = completed.stderr[-500:]
        return VerificationResult(
            passed=completed.returncode == 0,
            name=self.name,
            summary="command passed" if completed.returncode == 0 else "command failed",
            evidence={
                "returncode": completed.returncode,
                "stdout_tail": stdout_tail,
                "stderr_tail": stderr_tail,
                "command": " ".join(self.command),
            },
        )


class PytestVerifier(CommandVerifier):
    name = "pytest"

    def __init__(self, cwd: str | Path | None = None, timeout_seconds: float = 120.0, args: list[str] | None = None) -> None:
        super().__init__(["python", "-m", "pytest", *(args or [])], cwd=cwd, timeout_seconds=timeout_seconds)
