"""Self-review -- post-execution validation against deliverables.

The agent that executed a task evaluates its own output against the
deliverables set before execution.  The report is attached to the task
and used by the human/orchestrator reviewer to decide approve/reject.

Usage::

    report = self_review(
        task=task,
        files_modified=["src/foo.py", "tests/test_foo.py"],
        changes_summary="Added endpoint GET /users, wrote 3 tests.",
        reviewer="coding_orchestrator",
    )
    # report.passed == True if all deliverables are MET/SKIPPED
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from cohort.pipeline.deliverables import DeliverableStatus, update_deliverable
from cohort.pipeline.pipeline_task import PipelineTask


@dataclass
class SelfReviewReport:
    passed: bool
    reviewer: str
    reviewed_at: str
    summary: str
    deliverable_results: List[Dict[str, Any]] = field(default_factory=list)
    notes: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "SelfReviewReport":
        known = {f for f in cls.__dataclass_fields__}
        return cls(**{k: v for k, v in data.items() if k in known})


def self_review(
    task: PipelineTask,
    files_modified: List[str],
    changes_summary: str,
    reviewer: str = "agent",
    deliverable_evaluations: Optional[List[Dict[str, Any]]] = None,
    notes: Optional[str] = None,
) -> SelfReviewReport:
    """Attach a self-review report to a task after execution.

    Parameters
    ----------
    task:
        The PipelineTask that was just executed.
    files_modified:
        Actual files touched during execution.
    changes_summary:
        Human-readable summary of changes made.
    reviewer:
        ID of the agent or process doing the review.
    deliverable_evaluations:
        Optional list of ``{"name": str, "status": str, "notes": str}``
        to update individual deliverable statuses.  If omitted, all
        deliverables remain PENDING (reviewer must update manually).
    notes:
        Free-form notes from the reviewer.
    """
    task.files_modified = files_modified
    task.changes_summary = changes_summary

    # Apply any deliverable evaluations provided
    results: List[Dict[str, Any]] = []
    if deliverable_evaluations:
        for ev in deliverable_evaluations:
            name = ev.get("name", "")
            status = ev.get("status", DeliverableStatus.PENDING)
            ev_notes = ev.get("notes")
            update_deliverable(task, name, status, notes=ev_notes)
            results.append({"name": name, "status": status, "notes": ev_notes})

    # Determine pass/fail from deliverable statuses
    deliverables = task.deliverables
    if deliverables:
        passed = all(
            d.get("status") in (DeliverableStatus.MET, DeliverableStatus.SKIPPED)
            for d in deliverables
        )
    else:
        # No deliverables set -- review passes by default (bare queue usage)
        passed = True

    report = SelfReviewReport(
        passed=passed,
        reviewer=reviewer,
        reviewed_at=datetime.now(timezone.utc).isoformat(),
        summary=changes_summary,
        deliverable_results=results,
        notes=notes,
    )
    task.self_review_report = report.to_dict()
    return report
