"""Pipeline: compile → execute → return.

The simplest way to go from Prompt to LLM response in one call.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TypeVar

from pydantic import BaseModel

from .compiler import Compiler
from .executor import ExecutionConfig, ExecutionResult, Executor
from .tokens import TokenCounter
from .types import CompilationResult, Prompt, TokenBudget

T = TypeVar("T", bound=BaseModel)


@dataclass
class PipelineResult:
    """Full pipeline output: compilation report + execution result."""

    compilation: CompilationResult
    execution: ExecutionResult


class Pipeline:
    """End-to-end prompt compilation and execution.

    Usage:
        pipe = Pipeline(
            budget=TokenBudget(context_window=128_000),
            execution=ExecutionConfig(model="anthropic/claude-sonnet-4-5-20250514"),
        )

        # Simple
        result = pipe.run(prompt)
        print(result.execution.content)

        # Structured
        result = pipe.run(prompt, response_model=MySchema)
        print(result.execution.parsed)

        # Compile-only (no LLM call)
        compiled = pipe.compile(prompt)
        print(compiled.report())
    """

    def __init__(
        self,
        budget: TokenBudget,
        execution: ExecutionConfig | None = None,
        counter: TokenCounter | None = None,
    ) -> None:
        self.budget = budget
        self.compiler = Compiler(counter)
        self.executor = Executor(execution) if execution else None

    def compile(self, prompt: Prompt) -> CompilationResult:
        """Compile only. No LLM call."""
        return self.compiler.compile(prompt, self.budget)

    def run(
        self,
        prompt: Prompt,
        response_model: type[T] | None = None,
    ) -> PipelineResult:
        """Compile and execute.

        Args:
            prompt: The prompt source tree.
            response_model: Optional Pydantic model for structured output.

        Returns:
            PipelineResult with both compilation and execution details.
        """
        if self.executor is None:
            raise RuntimeError(
                "No ExecutionConfig provided. Use Pipeline(execution=ExecutionConfig(...)) "
                "or use pipeline.compile() for compile-only mode."
            )

        compiled = self.compile(prompt)
        result = self.executor.run(compiled, response_model=response_model)

        return PipelineResult(compilation=compiled, execution=result)
