"""
FSM Transitions for Project model.

This module defines declarative transitions for the Project entity,
replacing the previous signal-based approach with explicit, testable transitions.
"""

from typing import Any, Dict, Optional

from core.utils.common import load_func
from django.conf import settings
from fsm.registry import register_state_transition
from fsm.state_choices import ProjectStateChoices
from fsm.state_inference import get_or_infer_state
from fsm.state_manager import StateManager
from fsm.transitions import ModelChangeTransition, TransitionContext
from fsm.utils import get_or_initialize_state


@register_state_transition('project', 'project_created', triggers_on_create=True, triggers_on_update=False)
class ProjectCreatedTransition(ModelChangeTransition):
    """
    Transition when a new project is created.

    This is the initial state transition that occurs when a project is
    first saved to the database.

    Trigger: Automatically on creation (triggers_on_create=True, triggers_on_update=False)
    """

    def get_target_state(self, context: Optional[TransitionContext] = None) -> str:
        return ProjectStateChoices.CREATED

    def should_execute(self, context: TransitionContext) -> bool:
        """Only execute on creation, never on updates."""
        return self.is_creating

    def get_reason(self, context: TransitionContext) -> str:
        """Return detailed reason for project creation."""
        return 'Project created'

    def transition(self, context: TransitionContext) -> Dict[str, Any]:
        """
        Execute project creation transition.

        Args:
            context: Transition context containing project and user information

        Returns:
            Context data to store with the state record
        """
        project = context.entity

        return {
            'organization_id': project.organization_id,
            'title': project.title,
            'created_by_id': project.created_by_id if project.created_by_id else None,
            'label_config_present': bool(project.label_config),
        }


# Note: Project state transitions (IN_PROGRESS, COMPLETED) are triggered by task state changes
# via update_project_state_after_task_change() helper function, not by direct project model changes.


@register_state_transition('project', 'project_in_progress', triggers_on_create=False, triggers_on_update=False)
class ProjectInProgressTransition(ModelChangeTransition):
    """
    Transition when project moves to IN_PROGRESS state.

    Triggered when: First annotation is submitted on any task
    From: CREATED -> IN_PROGRESS
    """

    def get_target_state(self, context: Optional[TransitionContext] = None) -> str:
        return ProjectStateChoices.IN_PROGRESS

    def get_reason(self, context: TransitionContext) -> str:
        return 'Project moved to in progress - first annotation submitted'

    def transition(self, context: TransitionContext) -> Dict[str, Any]:
        project = context.entity
        return {
            'organization_id': project.organization_id,
            'total_tasks': project.tasks.count(),
        }


@register_state_transition('project', 'project_completed', triggers_on_create=False, triggers_on_update=False)
class ProjectCompletedTransition(ModelChangeTransition):
    """
    Transition when project moves to COMPLETED state.

    Triggered when: All tasks in project are COMPLETED
    From: IN_PROGRESS -> COMPLETED
    """

    def get_target_state(self, context: Optional[TransitionContext] = None) -> str:
        return ProjectStateChoices.COMPLETED

    def get_reason(self, context: TransitionContext) -> str:
        return 'Project completed - all tasks completed'

    def transition(self, context: TransitionContext) -> Dict[str, Any]:
        project = context.entity
        return {
            'organization_id': project.organization_id,
            'total_tasks': project.tasks.count(),
        }


@register_state_transition(
    'project', 'project_in_progress_from_completed', triggers_on_create=False, triggers_on_update=False
)
class ProjectInProgressFromCompletedTransition(ModelChangeTransition):
    """
    Transition when project moves back to IN_PROGRESS from COMPLETED.

    Triggered when: Any task becomes not COMPLETED (e.g., annotations deleted)
    From: COMPLETED -> IN_PROGRESS
    """

    def get_target_state(self, context: Optional[TransitionContext] = None) -> str:
        return ProjectStateChoices.IN_PROGRESS

    def get_reason(self, context: TransitionContext) -> str:
        return 'Project moved back to in progress - task became incomplete'

    def transition(self, context: TransitionContext) -> Dict[str, Any]:
        project = context.entity
        return {
            'organization_id': project.organization_id,
            'total_tasks': project.tasks.count(),
        }


def sync_project_state(project, user=None, reason=None, context_data=None):
    current_state = StateManager.get_current_state_value(project)
    inferred_state = get_or_infer_state(project)

    if current_state is None:
        get_or_initialize_state(project, user=user, inferred_state=inferred_state)
        return

    if current_state != inferred_state:
        if inferred_state == ProjectStateChoices.IN_PROGRESS:
            # Select in progress transition based on current state
            if current_state == ProjectStateChoices.CREATED:
                StateManager.execute_transition(entity=project, transition_name='project_in_progress', user=user)
            elif current_state == ProjectStateChoices.COMPLETED:
                StateManager.execute_transition(
                    entity=project, transition_name='project_in_progress_from_completed', user=user
                )
        elif inferred_state == ProjectStateChoices.COMPLETED:
            StateManager.execute_transition(entity=project, transition_name='project_completed', user=user)


def update_project_state_after_task_change(project, user=None):
    update_func = load_func(settings.FSM_SYNC_PROJECT_STATE)
    return update_func(project, user)
