"""Decision recording hook for the Strategize phase.

This module provides the ``StrategizeDecisionHook`` class, which integrates
decision recording into the Strategize phase of plan execution. The hook
captures every decision point during strategy decomposition, including:

- The question being answered
- The chosen option
- Alternatives considered
- Confidence score
- Rationale
- Full context snapshot (hot context hash, actor state reference, relevant resources)

The hook is designed to be called by the strategy actor during the Strategize
phase, recording decisions atomically with plan updates.

Based on:
    - docs/specification.md §Strategize-Phase Recording Loop
    - docs/adr/ADR-033-decision-recording-protocol.md
    - Forgejo issue #8522
"""

from __future__ import annotations

from typing import Any

import structlog

from cleveragents.application.ports.decision_recorder import DecisionRecorder
from cleveragents.application.services.decision_context import capture_context_snapshot
from cleveragents.core.exceptions import ValidationError
from cleveragents.domain.models.core.decision import (
    Decision,
    DecisionType,
)
from cleveragents.domain.models.core.plan import PlanPhase

logger = structlog.get_logger(__name__)


# ---------------------------------------------------------------------------
# Strategize decision hook
# ---------------------------------------------------------------------------


class StrategizeDecisionHook:
    """Hook for recording decisions during the Strategize phase.

    Integrates with the strategy actor to capture every decision point,
    including the question, chosen option, alternatives, confidence,
    rationale, and full context snapshot.

    The hook is designed to be called by the strategy actor during
    Strategize, and records decisions atomically with plan updates.

    Attributes:
        decision_service: The
            :class:`~cleveragents.application.ports.decision_recorder.DecisionRecorder`
            instance for persisting decisions.
        plan_id: ULID of the plan being strategized.
        parent_decision_id: Optional parent decision ID for tree structure.
    """

    def __init__(
        self,
        decision_service: DecisionRecorder,
        plan_id: str,
        parent_decision_id: str | None = None,
    ) -> None:
        """Initialize the Strategize decision hook.

        Args:
            decision_service: DecisionRecorder for recording decisions.
            plan_id: ULID of the plan.
            parent_decision_id: Optional parent decision ID.

        Raises:
            ValidationError: If plan_id is empty.
        """
        if not plan_id or not plan_id.strip():
            raise ValidationError("plan_id must not be empty")

        self.decision_service = decision_service
        self.plan_id = plan_id
        self.parent_decision_id = parent_decision_id
        self._logger = logger.bind(
            hook="strategize_decision",
            plan_id=plan_id,
        )

    def record_strategy_choice(
        self,
        question: str,
        chosen_option: str,
        alternatives_considered: list[str] | None = None,
        confidence_score: float | None = None,
        rationale: str = "",
        context_data: dict[str, Any] | None = None,
        actor_state: dict[str, Any] | None = None,
        relevant_resources: list[str] | None = None,
    ) -> Decision:
        """Record a strategy choice decision during Strategize.

        Args:
            question: What strategic question was being answered.
            chosen_option: The chosen approach.
            alternatives_considered: Other approaches evaluated.
            confidence_score: Confidence in the choice (0.0-1.0).
            rationale: Why this option was chosen.
            context_data: Current context window contents.
            actor_state: Actor's current state.
            relevant_resources: Resource IDs that influenced the decision.

        Returns:
            The recorded Decision.

        Raises:
            ValidationError: If required fields are missing.
        """
        if not question or not question.strip():
            raise ValidationError("question must not be empty")
        if not chosen_option or not chosen_option.strip():
            raise ValidationError("chosen_option must not be empty")

        snapshot = capture_context_snapshot(
            context_data=context_data,
            actor_state=actor_state,
            relevant_resources=relevant_resources,
        )

        self._logger.info(
            "Recording strategy choice decision",
            question=question,
            chosen_option=chosen_option,
            confidence=confidence_score,
        )

        try:
            decision = self.decision_service.record_decision(
                plan_id=self.plan_id,
                decision_type=DecisionType.STRATEGY_CHOICE,
                question=question,
                chosen_option=chosen_option,
                parent_decision_id=self.parent_decision_id,
                alternatives_considered=alternatives_considered,
                confidence_score=confidence_score,
                rationale=rationale,
                context_snapshot=snapshot,
                plan_phase=PlanPhase.STRATEGIZE,
            )
            self._logger.debug(
                "Strategy choice decision recorded",
                decision_id=decision.decision_id,
            )
            return decision
        except Exception as exc:
            self._logger.warning(
                "Failed to record strategy choice decision",
                error=str(exc),
                error_type=type(exc).__name__,
            )
            raise

    def record_resource_selection(
        self,
        question: str,
        chosen_option: str,
        alternatives_considered: list[str] | None = None,
        confidence_score: float | None = None,
        rationale: str = "",
        context_data: dict[str, Any] | None = None,
        actor_state: dict[str, Any] | None = None,
        relevant_resources: list[str] | None = None,
    ) -> Decision:
        """Record a resource selection decision during Strategize.

        Args:
            question: What resources should be selected.
            chosen_option: The selected resources.
            alternatives_considered: Other resource selections evaluated.
            confidence_score: Confidence in the selection (0.0-1.0).
            rationale: Why these resources were selected.
            context_data: Current context window contents.
            actor_state: Actor's current state.
            relevant_resources: Resource IDs that influenced the decision.

        Returns:
            The recorded Decision.

        Raises:
            ValidationError: If required fields are missing.
        """
        if not question or not question.strip():
            raise ValidationError("question must not be empty")
        if not chosen_option or not chosen_option.strip():
            raise ValidationError("chosen_option must not be empty")

        snapshot = capture_context_snapshot(
            context_data=context_data,
            actor_state=actor_state,
            relevant_resources=relevant_resources,
        )

        self._logger.info(
            "Recording resource selection decision",
            question=question,
            chosen_option=chosen_option,
        )

        try:
            decision = self.decision_service.record_decision(
                plan_id=self.plan_id,
                decision_type=DecisionType.RESOURCE_SELECTION,
                question=question,
                chosen_option=chosen_option,
                parent_decision_id=self.parent_decision_id,
                alternatives_considered=alternatives_considered,
                confidence_score=confidence_score,
                rationale=rationale,
                context_snapshot=snapshot,
                plan_phase=PlanPhase.STRATEGIZE,
            )
            self._logger.debug(
                "Resource selection decision recorded",
                decision_id=decision.decision_id,
            )
            return decision
        except Exception as exc:
            self._logger.warning(
                "Failed to record resource selection decision",
                error=str(exc),
                error_type=type(exc).__name__,
            )
            raise

    def record_subplan_spawn(
        self,
        question: str,
        chosen_option: str,
        alternatives_considered: list[str] | None = None,
        confidence_score: float | None = None,
        rationale: str = "",
        context_data: dict[str, Any] | None = None,
        actor_state: dict[str, Any] | None = None,
        relevant_resources: list[str] | None = None,
    ) -> Decision:
        """Record a subplan spawn decision during Strategize.

        Args:
            question: Why is a subplan being spawned.
            chosen_option: The subplan goal/description.
            alternatives_considered: Other decomposition approaches.
            confidence_score: Confidence in the decomposition (0.0-1.0).
            rationale: Why this decomposition was chosen.
            context_data: Current context window contents.
            actor_state: Actor's current state.
            relevant_resources: Resource IDs that influenced the decision.

        Returns:
            The recorded Decision.

        Raises:
            ValidationError: If required fields are missing.
        """
        if not question or not question.strip():
            raise ValidationError("question must not be empty")
        if not chosen_option or not chosen_option.strip():
            raise ValidationError("chosen_option must not be empty")

        snapshot = capture_context_snapshot(
            context_data=context_data,
            actor_state=actor_state,
            relevant_resources=relevant_resources,
        )

        self._logger.info(
            "Recording subplan spawn decision",
            question=question,
            chosen_option=chosen_option,
        )

        try:
            decision = self.decision_service.record_decision(
                plan_id=self.plan_id,
                decision_type=DecisionType.SUBPLAN_SPAWN,
                question=question,
                chosen_option=chosen_option,
                parent_decision_id=self.parent_decision_id,
                alternatives_considered=alternatives_considered,
                confidence_score=confidence_score,
                rationale=rationale,
                context_snapshot=snapshot,
                plan_phase=PlanPhase.STRATEGIZE,
            )
            self._logger.debug(
                "Subplan spawn decision recorded",
                decision_id=decision.decision_id,
            )
            return decision
        except Exception as exc:
            self._logger.warning(
                "Failed to record subplan spawn decision",
                error=str(exc),
                error_type=type(exc).__name__,
            )
            raise

    def record_invariant_enforced(
        self,
        question: str,
        chosen_option: str,
        alternatives_considered: list[str] | None = None,
        confidence_score: float | None = None,
        rationale: str = "",
        context_data: dict[str, Any] | None = None,
        actor_state: dict[str, Any] | None = None,
        relevant_resources: list[str] | None = None,
    ) -> Decision:
        """Record an invariant enforcement decision during Strategize.

        Args:
            question: What invariant is being enforced.
            chosen_option: How the invariant is being enforced.
            alternatives_considered: Other enforcement approaches evaluated.
            confidence_score: Confidence in the enforcement approach (0.0-1.0).
            rationale: Why this enforcement approach was chosen.
            context_data: Current context window contents.
            actor_state: Actor's current state.
            relevant_resources: Resource IDs that influenced the decision.

        Returns:
            The recorded Decision.

        Raises:
            ValidationError: If required fields are missing.
        """
        if not question or not question.strip():
            raise ValidationError("question must not be empty")
        if not chosen_option or not chosen_option.strip():
            raise ValidationError("chosen_option must not be empty")

        snapshot = capture_context_snapshot(
            context_data=context_data,
            actor_state=actor_state,
            relevant_resources=relevant_resources,
        )

        self._logger.info(
            "Recording invariant enforced decision",
            question=question,
            chosen_option=chosen_option,
        )

        try:
            decision = self.decision_service.record_decision(
                plan_id=self.plan_id,
                decision_type=DecisionType.INVARIANT_ENFORCED,
                question=question,
                chosen_option=chosen_option,
                parent_decision_id=self.parent_decision_id,
                alternatives_considered=alternatives_considered,
                confidence_score=confidence_score,
                rationale=rationale,
                context_snapshot=snapshot,
                plan_phase=PlanPhase.STRATEGIZE,
            )
            self._logger.debug(
                "Invariant enforced decision recorded",
                decision_id=decision.decision_id,
            )
            return decision
        except Exception as exc:
            self._logger.warning(
                "Failed to record invariant enforced decision",
                error=str(exc),
                error_type=type(exc).__name__,
            )
            raise
