"""
Activities for job execution tracking.

These activities handle creating execution records for scheduled jobs.
"""

from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Optional, Dict, Any
from temporalio import activity
import structlog
import uuid

logger = structlog.get_logger()


@dataclass
class ActivityCreateJobExecutionInput:
    """Input for creating job execution records"""
    execution_id: str
    job_id: Optional[str]
    organization_id: str
    entity_type: str  # "agent" or "team"
    entity_id: Optional[str]
    prompt: str
    trigger_type: str  # "cron", "webhook", "manual"
    trigger_metadata: Dict[str, Any]


@activity.defn
async def create_job_execution_record(input: ActivityCreateJobExecutionInput) -> dict:
    """
    Create execution and job_executions records for a scheduled job.

    This activity:
    1. Creates execution record in executions table
    2. Creates job_executions junction record
    3. Returns execution metadata

    Args:
        input: Execution creation input

    Returns:
        Dict with execution_id and status
    """
    from control_plane_api.app.lib.supabase import get_supabase

    supabase = get_supabase()
    now = datetime.now(timezone.utc).isoformat()

    logger.info(
        "creating_job_execution_records",
        execution_id=input.execution_id,
        job_id=input.job_id,
        trigger_type=input.trigger_type,
    )

    try:
        # Get entity name for display
        entity_name = None
        runner_name = None

        if input.entity_id and input.entity_type:
            entity_table = f"{input.entity_type}s"  # agent -> agents, team -> teams
            try:
                entity_result = supabase.table(entity_table).select("name, runner_name").eq("id", input.entity_id).maybe_single().execute()
                if entity_result.data:
                    entity_name = entity_result.data.get("name")
                    runner_name = entity_result.data.get("runner_name")
            except Exception as e:
                logger.warning(
                    "failed_to_get_entity_name",
                    entity_type=input.entity_type,
                    entity_id=input.entity_id,
                    error=str(e)
                )

        # Map trigger_type to trigger_source
        trigger_source_map = {
            "manual": "job_manual",
            "cron": "job_cron",
            "webhook": "job_webhook",
        }
        trigger_source = trigger_source_map.get(input.trigger_type, "job_cron")

        # Determine execution_type
        execution_type_value = input.entity_type.upper() if input.entity_type else "AGENT"

        # Create execution record
        execution_record = {
            "id": input.execution_id,
            "organization_id": input.organization_id,
            "execution_type": execution_type_value,
            "entity_id": input.entity_id,
            "entity_name": entity_name,
            "runner_name": runner_name,
            "trigger_source": trigger_source,
            "trigger_metadata": {
                "job_id": input.job_id,
                "job_name": input.trigger_metadata.get("job_name"),
                "trigger_type": input.trigger_type,
                **input.trigger_metadata,
            },
            "user_id": input.trigger_metadata.get("user_id"),
            "user_email": input.trigger_metadata.get("user_email"),
            "user_name": input.trigger_metadata.get("user_name"),
            "user_avatar": input.trigger_metadata.get("user_avatar"),
            "status": "pending",
            "prompt": input.prompt,
            "created_at": now,
            "updated_at": now,
            "execution_metadata": {
                "job_id": input.job_id,
                "job_name": input.trigger_metadata.get("job_name"),
                "trigger_type": input.trigger_type,
                "scheduled_execution": True,
                **input.trigger_metadata,
            },
        }

        # Insert execution record
        supabase.table("executions").insert(execution_record).execute()

        logger.info(
            "created_execution_record",
            execution_id=input.execution_id,
            job_id=input.job_id,
        )

        # Create job_executions junction record if job_id provided
        if input.job_id:
            job_execution_record = {
                "id": str(uuid.uuid4()),
                "job_id": input.job_id,
                "execution_id": input.execution_id,
                "organization_id": input.organization_id,
                "trigger_type": input.trigger_type,
                "trigger_metadata": input.trigger_metadata,
                "execution_status": "pending",
                "created_at": now,
            }
            supabase.table("job_executions").insert(job_execution_record).execute()

            logger.info(
                "created_job_execution_junction_record",
                execution_id=input.execution_id,
                job_id=input.job_id,
            )

            # Update job's last_execution_id and increment total_executions
            # Note: We use RPC to atomically increment the counter
            try:
                # Get job details to calculate next execution
                job_result = supabase.table("jobs").select("cron_schedule, cron_timezone, trigger_type").eq("id", input.job_id).maybe_single().execute()

                update_data = {
                    "last_execution_id": input.execution_id,
                    "last_execution_at": now,
                    "total_executions": supabase.rpc("increment", {"row_id": input.job_id, "column": "total_executions", "value": 1}),
                }

                # Calculate next_execution_at for cron jobs
                if job_result.data and job_result.data.get("trigger_type") == "cron":
                    cron_schedule = job_result.data.get("cron_schedule")
                    if cron_schedule:
                        from croniter import croniter
                        cron_iter = croniter(cron_schedule, datetime.now(timezone.utc))
                        next_execution = cron_iter.get_next(datetime)
                        update_data["next_execution_at"] = next_execution.isoformat()

                        logger.info(
                            "calculated_next_execution_time",
                            job_id=input.job_id,
                            next_execution_at=update_data["next_execution_at"],
                        )

                supabase.table("jobs").update(update_data).eq("id", input.job_id).execute()

                logger.info(
                    "updated_job_execution_counts",
                    job_id=input.job_id,
                    execution_id=input.execution_id,
                )
            except Exception as e:
                # Just log and continue - this is not critical
                logger.warning(
                    "failed_to_update_job_counts",
                    job_id=input.job_id,
                    error=str(e),
                )

        return {
            "execution_id": input.execution_id,
            "status": "created",
            "created_at": now,
        }

    except Exception as e:
        logger.error(
            "failed_to_create_job_execution_records",
            execution_id=input.execution_id,
            job_id=input.job_id,
            error=str(e),
            exc_info=True,
        )
        raise


@activity.defn
async def update_job_execution_status(
    job_id: str,
    execution_id: str,
    status: str,
    duration_ms: Optional[int] = None,
    error_message: Optional[str] = None,
) -> dict:
    """
    Update job_executions record with execution results.

    Args:
        job_id: Job ID
        execution_id: Execution ID
        status: Final status (completed/failed)
        duration_ms: Execution duration in milliseconds
        error_message: Error message if failed

    Returns:
        Dict with update status
    """
    from control_plane_api.app.lib.supabase import get_supabase

    supabase = get_supabase()
    now = datetime.now(timezone.utc).isoformat()

    logger.info(
        "updating_job_execution_status",
        job_id=job_id,
        execution_id=execution_id,
        status=status,
    )

    try:
        # Update job_executions record
        update_data = {
            "execution_status": status,
            "execution_duration_ms": duration_ms,
            "updated_at": now,
        }

        supabase.table("job_executions").update(update_data).eq(
            "job_id", job_id
        ).eq(
            "execution_id", execution_id
        ).execute()

        # Update job success/failure counts
        if status == "completed":
            # Increment successful_executions
            try:
                supabase.rpc("increment_job_success", {"job_id": job_id}).execute()
            except Exception:
                # Fallback to manual update
                result = supabase.table("jobs").select("successful_executions").eq("id", job_id).single().execute()
                if result.data:
                    current = result.data.get("successful_executions", 0)
                    supabase.table("jobs").update({"successful_executions": current + 1}).eq("id", job_id).execute()

        elif status == "failed":
            # Increment failed_executions
            try:
                supabase.rpc("increment_job_failure", {"job_id": job_id}).execute()
            except Exception:
                # Fallback to manual update
                result = supabase.table("jobs").select("failed_executions").eq("id", job_id).single().execute()
                if result.data:
                    current = result.data.get("failed_executions", 0)
                    supabase.table("jobs").update({"failed_executions": current + 1}).eq("id", job_id).execute()

        logger.info(
            "updated_job_execution_status",
            job_id=job_id,
            execution_id=execution_id,
            status=status,
        )

        return {
            "job_id": job_id,
            "execution_id": execution_id,
            "status": "updated",
        }

    except Exception as e:
        logger.error(
            "failed_to_update_job_execution_status",
            job_id=job_id,
            execution_id=execution_id,
            error=str(e),
            exc_info=True,
        )
        raise
