import pathlib
from dataclasses import dataclass

import rich.box
import rich.console
import rich.live
import rich.panel
import rich.table
import typer

from rendercv.exception import RenderCVUserError, RenderCVValidationError


def format_validation_error_location(error_object: RenderCVValidationError) -> str:
    """Format schema/YAML location for validation error table rows.

    Why:
        YAML parsing errors don't have schema locations, so we show source file
        and line/column coordinates to keep the location column actionable.

    Args:
        error_object: Validation error with schema and YAML location metadata.

    Returns:
        Human-readable location string for table display.
    """
    if error_object.schema_location is not None:
        return ".".join(error_object.schema_location)

    if error_object.yaml_location is None:
        return error_object.yaml_source

    (start_line, _), (end_line, _) = error_object.yaml_location
    if start_line == end_line:
        return f"{error_object.yaml_source}: line {start_line}"
    return f"{error_object.yaml_source}: line {start_line} to line {end_line}"


class ProgressPanel(rich.live.Live):
    """Live-updating terminal panel showing CV generation progress with timing.

    Example:
        ```py
        with ProgressPanel(quiet=False) as progress:
            progress.update_progress("50", "Generated PDF", [Path("cv.pdf")])
            progress.finish_progress()
        # Displays: ✓ 50 ms   Generated PDF: ./cv.pdf
        ```

    Args:
        quiet: Suppress all terminal output.
    """

    def __init__(self, quiet: bool = False):
        self.completed_steps: list[CompletedStep] = []
        super().__init__(
            rich.panel.Panel(
                "...",
                title="Rendering your CV...",
                title_align="left",
                border_style="bright_black",
            ),
            console=rich.console.Console(quiet=quiet),
            refresh_per_second=4,
        )

    def update_progress(
        self, time_took: str, message: str, paths: list[pathlib.Path]
    ) -> None:
        """Add completed step to progress display.

        Args:
            time_took: Execution time in milliseconds as string.
            message: Step description.
            paths: Generated file paths to display.
        """
        self.completed_steps.append(CompletedStep(time_took, message, paths))
        self.print_progress_panel(title="Rendering your CV...")

    def finish_progress(self) -> None:
        """Display final success panel and clear state."""
        self.print_progress_panel(title="Your CV is ready")
        self.completed_steps.clear()

    def print_progress_panel(self, title: str) -> None:
        """Render progress panel with all completed steps.

        Args:
            title: Panel title text.
        """
        lines: list[str] = []
        for step in self.completed_steps:
            paths_str = ""
            if step.paths:
                try:
                    paths = [
                        path.relative_to(pathlib.Path.cwd()) for path in step.paths
                    ]
                except ValueError:
                    paths = step.paths
                paths_as_strings = [f"./{path}" for path in paths]
                paths_str = "; ".join(paths_as_strings)

            timing = f"[bold green]{step.timing_ms + ' ms':<8}[/bold green]"
            message = step.message + (": " if paths_str else ".")
            paths_display = f"[purple]{paths_str}[/purple]" if paths_str else ""
            lines.append(f"[green]✓[/green] {timing} {message:<26} {paths_display}")

        content = "\n".join(lines) if lines else "Rendering..."

        self.update(
            rich.panel.Panel(
                content,
                title=title,
                title_align="left",
                border_style="bright_black",
            )
        )

    def print_user_error(self, user_error: RenderCVUserError) -> None:
        """Display error panel and exit with error code.

        Args:
            user_error: User-facing error to display.
        """
        self.clear()
        self.update(
            rich.panel.Panel(
                user_error.message or "An unknown error occurred.",
                title="[bold red]Error[/bold red]",
                title_align="left",
                border_style="bold red",
            )
        )
        raise typer.Exit(code=1)

    def print_validation_errors(self, errors: list[RenderCVValidationError]) -> None:
        """Display validation errors in table format and exit.

        Why:
            Pydantic validation errors are parsed into user-friendly messages with
            YAML locations. Table shows exactly which field failed and why.

        Args:
            errors: List of validation errors with location, input, and message.
        """
        self.completed_steps.clear()
        table = rich.table.Table(expand=True, show_lines=True, box=rich.box.ROUNDED)
        table.add_column("Location", style="cyan", no_wrap=True)
        table.add_column("Input Value", style="magenta", no_wrap=True)
        table.add_column("Explanation", style="orange4")

        for error_object in errors:
            table.add_row(
                format_validation_error_location(error_object),
                error_object.input,
                error_object.message,
            )

        self.update(
            rich.panel.Panel(
                table,
                title="[bold red]There are validation errors![/bold red]",
                title_align="left",
                border_style="bold red",
            )
        )

        raise typer.Exit(code=1)

    def clear(self) -> None:
        """Clear all completed steps and panel display."""
        self.completed_steps.clear()
        self.update("")


@dataclass
class CompletedStep:
    timing_ms: str
    message: str
    paths: list[pathlib.Path]
