"""Application service for feedback and outcome capture."""
from __future__ import annotations

from typing import Any
from uuid import uuid4

from .coordinator import LearningCoordinator, MemoryLearningTarget
from .models import FeedbackRecord, OutcomeRecord, validate_failure_types
from .store import LearningStore


class FeedbackAPI:
    """High-level API for recording feedback and outcomes with trace linkage."""

    def __init__(
        self,
        store: LearningStore,
        *,
        memory_targets: list[MemoryLearningTarget] | None = None,
        coordinator: LearningCoordinator | None = None,
    ) -> None:
        self.store = store
        self.coordinator = coordinator or (
            LearningCoordinator(store, memory_targets=memory_targets)
            if memory_targets
            else None
        )

    def _resolve_run(self, *, run_id: str | None, trace_id: str | None) -> Any | None:
        if run_id is not None:
            return self.store.get_run(run_id)
        if trace_id is not None:
            return self.store.get_run_by_trace(trace_id)
        return None

    def record_feedback(
        self,
        *,
        kind: str,
        run_id: str | None = None,
        trace_id: str | None = None,
        rating: float | None = None,
        suggestion_outcome: str | None = None,
        corrected_output: str | None = None,
        correction_notes: str | None = None,
        expected_output: str | None = None,
        failure_types: list[str] | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> FeedbackRecord:
        validate_failure_types(failure_types or [])
        run = self._resolve_run(run_id=run_id, trace_id=trace_id)
        resolved_trace_id = getattr(run, "trace_id", None) or trace_id
        if resolved_trace_id is None:
            raise ValueError("run_id or trace_id must resolve to a captured run")
        record = FeedbackRecord(
            id=uuid4().hex,
            kind=kind,
            run_id=getattr(run, "id", None) or run_id,
            trace_id=resolved_trace_id,
            request_id=getattr(run, "request_id", None),
            session_id=getattr(run, "session_id", None),
            workspace_id=getattr(run, "workspace_id", None),
            agent_name=getattr(run, "agent_name", None),
            prompt_version=getattr(run, "prompt_version", "unversioned"),
            model=getattr(run, "model", None),
            config_fingerprint=getattr(run, "config_fingerprint", None),
            rating=rating,
            suggestion_outcome=suggestion_outcome,
            corrected_output=corrected_output,
            correction_notes=correction_notes,
            expected_output=expected_output,
            failure_types=failure_types or [],
            metadata=metadata or {},
        )
        self.store.create_feedback(record)
        if self.coordinator is not None:
            self.coordinator.after_feedback(record=record, run=run)
        return record

    def record_outcome(
        self,
        *,
        kind: str,
        run_id: str | None = None,
        trace_id: str | None = None,
        value: float | int | bool | str | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> OutcomeRecord:
        run = self._resolve_run(run_id=run_id, trace_id=trace_id)
        resolved_trace_id = getattr(run, "trace_id", None) or trace_id
        if resolved_trace_id is None:
            raise ValueError("run_id or trace_id must resolve to a captured run")
        record = OutcomeRecord(
            id=uuid4().hex,
            kind=kind,
            run_id=getattr(run, "id", None) or run_id,
            trace_id=resolved_trace_id,
            request_id=getattr(run, "request_id", None),
            session_id=getattr(run, "session_id", None),
            workspace_id=getattr(run, "workspace_id", None),
            agent_name=getattr(run, "agent_name", None),
            prompt_version=getattr(run, "prompt_version", "unversioned"),
            model=getattr(run, "model", None),
            config_fingerprint=getattr(run, "config_fingerprint", None),
            value=value,
            metadata=metadata or {},
        )
        self.store.create_outcome(record)
        if self.coordinator is not None:
            self.coordinator.after_outcome(record=record, run=run)
        return record
