#!/usr/bin/env python3
"""Custom exceptions for test failures that need to bubble up to callers."""

import subprocess
from dataclasses import dataclass
from typing import Optional


@dataclass
class TestFailureInfo:
    """Information about a single test failure"""

    test_name: str
    command: str
    return_code: int
    output: str
    error_type: str = "test_failure"


class FastLEDTestException(Exception):
    """Base exception for FastLED test failures"""

    def __init__(self, message: str, failures: Optional[list[TestFailureInfo]] = None):
        super().__init__(message)
        self.failures = failures or []
        self.message = message

    def add_failure(self, failure: TestFailureInfo) -> None:
        """Add a test failure to this exception"""
        self.failures.append(failure)

    def has_failures(self) -> bool:
        """Check if this exception contains any failures"""
        return len(self.failures) > 0

    def get_failure_summary(self) -> str:
        """Get a summary of all failures"""
        if not self.failures:
            return self.message

        summary = [self.message]
        summary.append(f"\nFailed tests ({len(self.failures)}):")
        for failure in self.failures:
            summary.append(
                f"  - {failure.test_name}: {failure.error_type} (exit code {failure.return_code})"
            )

        return "\n".join(summary)

    def get_detailed_failure_info(self) -> str:
        """Get detailed information about all failures"""
        if not self.failures:
            return self.message

        details = [self.message]
        details.append(f"\n{'=' * 50}")
        details.append("DETAILED FAILURE INFORMATION")
        details.append(f"{'=' * 50}")

        for i, failure in enumerate(self.failures, 1):
            cmd_str: str = (
                subprocess.list2cmdline(failure.command)
                if isinstance(failure.command, list)
                else failure.command
            )
            assert isinstance(cmd_str, str)
            details.append(f"\n{i}. {failure.test_name}")
            details.append(f"   Command: {cmd_str}")
            details.append(f"   Error Type: {failure.error_type}")
            details.append(f"   Exit Code: {failure.return_code}")
            details.append("   Output:")
            # Indent the output
            for line in failure.output.split("\n"):
                details.append(f"     {line}")

        return "\n".join(details)


class CompilationFailedException(FastLEDTestException):
    """Exception for compilation failures"""

    def __init__(
        self,
        message: str = "Compilation failed",
        failures: Optional[list[TestFailureInfo]] = None,
    ):
        super().__init__(message, failures)


class TestExecutionFailedException(FastLEDTestException):
    """Exception for test execution failures"""

    def __init__(
        self,
        message: str = "Test execution failed",
        failures: Optional[list[TestFailureInfo]] = None,
    ):
        super().__init__(message, failures)


class TestTimeoutException(FastLEDTestException):
    """Exception for test timeouts"""

    def __init__(
        self,
        message: str = "Test execution timed out",
        failures: Optional[list[TestFailureInfo]] = None,
    ):
        super().__init__(message, failures)
