"""BPM write-side services — all mutations go through here."""

from __future__ import annotations

from datetime import date
from decimal import Decimal
from typing import Any
from uuid import UUID

from django.db import transaction
from django.utils import timezone

from simorgh.apps.bpm.models import (
    ControlPointExecution,
    ControlPointExecutionOutcome,
    ProcessControlPoint,
    ProcessDefinition,
    ProcessInstance,
    ProcessInstanceRuleCheck,
    ProcessInstanceStep,
    ProcessKPI,
    ProcessKPIMeasurement,
    ProcessRole,
    RACIEntry,
    RACIMatrix,
    StepStatus,
)


# ---------------------------------------------------------------------------
# Process services
# ---------------------------------------------------------------------------


@transaction.atomic
def create_process(tenant, validated_data: dict[str, Any]) -> ProcessDefinition:
    """Create a new ProcessDefinition owned by *tenant*."""
    return ProcessDefinition.objects.create(tenant=tenant, **validated_data)


@transaction.atomic
def update_process(process: ProcessDefinition, validated_data: dict[str, Any]) -> ProcessDefinition:
    for attr, value in validated_data.items():
        setattr(process, attr, value)
    process.save()
    return process


# ---------------------------------------------------------------------------
# RACI services
# ---------------------------------------------------------------------------


@transaction.atomic
def bulk_upsert_raci(process: ProcessDefinition, entries_data: list[dict]) -> list[RACIEntry]:
    """
    Replace RACI entries for *process* with the provided list.

    Each entry dict must have: step_id, role_id, responsibility, activity_label (optional).
    """
    matrix, _ = RACIMatrix.objects.get_or_create(process=process)

    # Clear existing entries
    RACIEntry.objects.filter(matrix=matrix).delete()

    created = []
    for item in entries_data:
        entry = RACIEntry.objects.create(
            matrix=matrix,
            step_id=item["step_id"],
            role_id=item["role_id"],
            responsibility=item["responsibility"],
            activity_label=item.get("activity_label", ""),
            notes=item.get("notes", ""),
        )
        created.append(entry)
    return created


# ---------------------------------------------------------------------------
# Instance services
# ---------------------------------------------------------------------------


@transaction.atomic
def create_instance(
    tenant,
    process: ProcessDefinition,
    validated_data: dict[str, Any],
    user,
) -> ProcessInstance:
    """Create a new process execution instance and seed its step records."""
    instance = ProcessInstance.objects.create(
        tenant=tenant,
        process=process,
        created_by=user,
        **validated_data,
    )

    # Auto-create step tracker rows for every operational step
    for op_step in process.operational_steps.order_by("order"):
        ProcessInstanceStep.objects.create(
            instance=instance,
            step=op_step,
            status=StepStatus.PENDING,
        )

    return instance


@transaction.atomic
def update_instance_step(
    instance: ProcessInstance,
    step_id: int,
    validated_data: dict[str, Any],
) -> ProcessInstanceStep:
    """Update status / assigned_to / notes on a single instance step."""
    inst_step = ProcessInstanceStep.objects.get(instance=instance, step_id=step_id)
    for attr, value in validated_data.items():
        setattr(inst_step, attr, value)
    inst_step.save()
    return inst_step


@transaction.atomic
def execute_control_point(
    instance: ProcessInstance,
    control_point: ProcessControlPoint,
    outcome: str,
    user,
    notes: str = "",
) -> ControlPointExecution:
    """Record a control-point execution outcome for an instance."""
    execution, _ = ControlPointExecution.objects.update_or_create(
        instance=instance,
        control_point=control_point,
        defaults={
            "outcome": outcome,
            "executed_at": timezone.now(),
            "executed_by": user,
            "notes": notes,
        },
    )
    return execution


# ---------------------------------------------------------------------------
# KPI services
# ---------------------------------------------------------------------------


@transaction.atomic
def record_kpi_measurement(
    kpi: ProcessKPI,
    value: Decimal,
    user,
    notes: str = "",
    process_instance_id: UUID | None = None,
) -> ProcessKPIMeasurement:
    """Record a measurement and auto-create an alert if target is not met."""
    from simorgh.apps.bpm.models import KPIAlert  # noqa: PLC0415

    measurement = ProcessKPIMeasurement.objects.create(
        kpi=kpi,
        value=value,
        measured_by=user,
        notes=notes,
        process_instance_id=process_instance_id,
    )

    if not kpi.evaluate(value):
        from simorgh.apps.bpm.models.kpi import KPIAlertType  # noqa: PLC0415

        alert_type = (
            KPIAlertType.BELOW_TARGET
            if value < kpi.target_value
            else KPIAlertType.ABOVE_TARGET
        )
        KPIAlert.objects.create(
            kpi=kpi,
            measurement=measurement,
            alert_type=alert_type,
        )

    return measurement


# ---------------------------------------------------------------------------
# Benchmarking (Phase 13)
# ---------------------------------------------------------------------------

def create_benchmark_comparison(*, process, kpi, benchmark, org_value, user, notes=""):
    """Create an OrgBenchmarkComparison (auto-computes percentile_position and gap_to_median)."""
    import datetime  # noqa: PLC0415
    from simorgh.apps.bpm.models import OrgBenchmarkComparison  # noqa: PLC0415

    comparison = OrgBenchmarkComparison(
        process=process,
        kpi=kpi,
        benchmark=benchmark,
        org_value=org_value,
        assessment_date=datetime.date.today(),
        assessed_by=user,
        notes=notes,
    )
    comparison.save()  # save() auto-computes percentile_position and gap_to_median
    return comparison


# ---------------------------------------------------------------------------
# Maturity Assessment (Phase 14)
# ---------------------------------------------------------------------------

def create_maturity_assessment(*, process, assessed_by, current_level, target_level,
                                dimension_scores=None, improvement_notes="",
                                next_review_date=None):
    """Create a ProcessMaturityAssessment."""
    import datetime  # noqa: PLC0415
    from simorgh.apps.bpm.models import ProcessMaturityAssessment  # noqa: PLC0415

    return ProcessMaturityAssessment.objects.create(
        process=process,
        assessed_by=assessed_by,
        assessment_date=datetime.date.today(),
        current_level=current_level,
        target_level=target_level,
        dimension_scores=dimension_scores or {},
        improvement_notes=improvement_notes,
        next_review_date=next_review_date,
    )


def update_maturity_assessment(assessment, *, current_level=None, target_level=None,
                                dimension_scores=None, improvement_notes=None,
                                next_review_date=None):
    """Partially update a ProcessMaturityAssessment."""
    if current_level is not None:
        assessment.current_level = current_level
    if target_level is not None:
        assessment.target_level = target_level
    if dimension_scores is not None:
        assessment.dimension_scores = dimension_scores
    if improvement_notes is not None:
        assessment.improvement_notes = improvement_notes
    if next_review_date is not None:
        assessment.next_review_date = next_review_date
    assessment.save()
    return assessment


def create_improvement_action(*, assessment, title, description="", target_level,
                               due_date=None, owner=None):
    """Create a MaturityImprovementAction for an assessment."""
    from simorgh.apps.bpm.models import MaturityImprovementAction  # noqa: PLC0415

    return MaturityImprovementAction.objects.create(
        assessment=assessment,
        title=title,
        description=description,
        target_level=target_level,
        due_date=due_date,
        owner=owner,
    )


def update_improvement_action_status(action, status):
    """Update the status of a MaturityImprovementAction."""
    from simorgh.apps.bpm.models import ActionStatus  # noqa: PLC0415

    if status not in ActionStatus.values:
        raise ValueError(f"Invalid status: {status!r}")
    action.status = status
    action.save(update_fields=["status", "updated_at"])
    return action
