"""
Status updater for worker.

Single Responsibility: Update job status in DynamoDB.
"""

import os
import urllib.request
import json
from datetime import datetime
from typing import Any

from ..clients.dynamodb_client import DynamoDBClient


class StatusUpdater:
    """
    Update job status in DynamoDB.

    Handles status transitions and observability tracking.
    Max 8 methods per CLAUDE.md guidelines.
    """

    def __init__(self, dynamodb_client: DynamoDBClient):
        """
        Initialize status updater.

        Args:
            dynamodb_client: DynamoDB client for updates
        """
        self.dynamodb_client = dynamodb_client

    def update_to_running(self, job_id: str) -> None:
        """
        Update job status to running with ECS task metadata.

        Args:
            job_id: Job identifier

        Raises:
            RuntimeError: If update fails
        """
        updates = {
            "status": "running",
            "started_at": datetime.utcnow().isoformat(),
            "last_heartbeat": datetime.utcnow().isoformat(),
        }

        # Capture ECS task ID from metadata endpoint
        task_id = self._get_ecs_task_id()
        if task_id:
            updates["task_id"] = task_id

        self.dynamodb_client.update_job(job_id=job_id, updates=updates)

    def update_to_completed(
        self, job_id: str, result: dict[str, Any]
    ) -> None:
        """
        Update job status to completed with result.

        Args:
            job_id: Job identifier
            result: Processing result metadata

        Raises:
            RuntimeError: If update fails
        """
        self.dynamodb_client.update_job(
            job_id=job_id,
            updates={
                "status": "completed",
                "completed_at": datetime.utcnow().isoformat(),
                "result": result,
            },
        )

    def update_to_failed(self, job_id: str, error: str, error_details: dict[str, Any] | None = None) -> None:
        """
        Update job status to failed with error message and optional structured details.

        Args:
            job_id: Job identifier
            error: Error message or traceback
            error_details: Optional structured error information with keys:
                - stage: str (e.g., "usage", "metadata")
                - error_type: str (e.g., "ConfigError", "ValidationError")
                - message: str (user-friendly error message)
                - field: str | None (config field that caused error)
                - suggestion: str | None (how to fix the error)
                - traceback: str (full Python traceback)

        Raises:
            RuntimeError: If update fails
        """
        updates = {
            "status": "failed",
            "completed_at": datetime.utcnow().isoformat(),
            "error": error,
        }

        if error_details:
            updates["error_details"] = error_details

        self.dynamodb_client.update_job(job_id=job_id, updates=updates)

    def update_dataset_info(
        self, job_id: str, dataset_name: str, description: str = ""
    ) -> None:
        """
        Update dataset name and description.

        Called after config is loaded or dataset is created.

        Args:
            job_id: Job identifier
            dataset_name: Dataset identifier
            description: Optional dataset description

        Raises:
            RuntimeError: If update fails
        """
        updates = {"dataset_name": dataset_name}
        if description:
            updates["dataset_description"] = description

        self.dynamodb_client.update_job(job_id=job_id, updates=updates)

    def add_processing_stage(
        self,
        job_id: str,
        stage_name: str,
        progress: dict[str, Any] | None = None,
        status: str = "in_progress"
    ) -> None:
        """
        Add processing stage breadcrumb with optional progress tracking.

        Creates audit trail of processing steps for debugging and user visibility.

        Args:
            job_id: Job identifier
            stage_name: Name of processing stage (e.g., "config_loaded", "processing_aws_2025_09")
            progress: Optional progress information with keys:
                - current: int (items processed so far)
                - total: int (total items to process)
                - percent: float (percentage complete, 0-100)
            status: Stage status ("in_progress", "completed", "failed")

        Raises:
            RuntimeError: If update fails
        """
        # Get current job to append to processing_stages list
        job = self.dynamodb_client.get_job(job_id)
        if not job:
            raise RuntimeError(f"Job {job_id} not found")

        # Build stage entry
        stage_entry = {
            "stage": stage_name,
            "status": status,
            "timestamp": datetime.utcnow().isoformat()
        }

        if progress:
            stage_entry["progress"] = progress

        # Append new stage to processing_stages list
        stages = job.get("processing_stages", [])
        stages.append(stage_entry)

        self.dynamodb_client.update_job(
            job_id=job_id,
            updates={
                "processing_stages": stages,
                "last_heartbeat": datetime.utcnow().isoformat()
            }
        )

    def update_heartbeat(self, job_id: str) -> None:
        """
        Update heartbeat timestamp.

        Called periodically during long-running processing
        to show worker is alive.

        Args:
            job_id: Job identifier

        Raises:
            RuntimeError: If update fails
        """
        self.dynamodb_client.update_job(
            job_id=job_id,
            updates={"last_heartbeat": datetime.utcnow().isoformat()},
        )

    def get_job_description(self, job_id: str) -> str:
        """
        Get user-provided description for the job.

        Args:
            job_id: Job identifier

        Returns:
            Job description string, or default if not found

        Raises:
            RuntimeError: If job not found
        """
        job = self.dynamodb_client.get_job(job_id)
        if not job:
            raise RuntimeError(f"Job {job_id} not found")

        return job.get("description", "No description provided")

    def _get_ecs_task_id(self) -> str | None:
        """
        Get ECS task ID from metadata endpoint.

        Returns:
            Task ID (short form) or None if not running in ECS
        """
        # Debug: Print all ECS-related environment variables
        print("DEBUG: Checking for ECS metadata...")
        for key, value in os.environ.items():
            if "ECS" in key or "METADATA" in key:
                print(f"  {key}={value}")

        # Try ECS metadata endpoint V4 (environment variable)
        metadata_uri = os.environ.get("ECS_CONTAINER_METADATA_URI_V4")
        if metadata_uri:
            print(f"DEBUG: Found metadata URI: {metadata_uri}")
            task_id = self._fetch_from_endpoint(f"{metadata_uri}/task")
            if task_id:
                return task_id

        # Try ECS metadata endpoint V4 (direct URL - Fargate)
        print("DEBUG: Trying direct endpoint 169.254.170.2...")
        task_id = self._fetch_from_endpoint("http://169.254.170.2/v4/task")
        if task_id:
            return task_id

        print("DEBUG: No task ID found")
        return None

    def _fetch_from_endpoint(self, url: str) -> str | None:
        """
        Fetch task ID from metadata endpoint URL.

        Args:
            url: Metadata endpoint URL

        Returns:
            Task ID or None if fetch fails
        """
        try:
            with urllib.request.urlopen(url, timeout=2) as response:
                task_metadata = json.loads(response.read())
                task_arn = task_metadata.get("TaskARN", "")
                # Extract task ID from ARN
                if task_arn:
                    task_id = task_arn.split("/")[-1]
                    print(f"DEBUG: Extracted task ID: {task_id}")
                    return task_id
        except Exception as e:
            print(f"DEBUG: Failed to fetch from {url}: {e}")
        return None
