"""
Job submission service.

Single Responsibility: Coordinate job creation across S3, DynamoDB, and SQS.
"""

import uuid
from datetime import datetime
from typing import Any

from ..clients.dynamodb_client import DynamoDBClient
from ..clients.s3_client import S3Client
from ..clients.sqs_client import SQSClient


class JobSubmissionService:
    """
    Handle job submission workflow.

    Orchestrates config upload, state creation, and queue enqueuing.
    Max 5 methods per CLAUDE.md guidelines.
    """

    def __init__(
        self,
        s3_client: S3Client,
        dynamodb_client: DynamoDBClient,
        sqs_client: SQSClient,
    ):
        """
        Initialize job submission service.

        Args:
            s3_client: S3 client for config uploads
            dynamodb_client: DynamoDB client for state management
            sqs_client: SQS client for job queuing
        """
        self.s3_client = s3_client
        self.dynamodb_client = dynamodb_client
        self.sqs_client = sqs_client

    def submit_job(
        self,
        job_type: str,
        dataset_name: str,
        user_id: str,
        config_content: str | None = None,
        config_s3_path: str | None = None,
        config_type: str = "file",
        description: str = "",
        recreate_connections: bool = False,
    ) -> str:
        """
        Submit job for processing.

        Orchestrates: ID generation → config upload → DynamoDB record → SQS queue.
        Config sources: inline content (uploaded) or S3 path reference.
        """
        job_id = self._generate_job_id()
        final_config_s3_path = self._resolve_config_path(
            job_id, job_type, config_content, config_s3_path
        )
        self._persist_job(
            job_id=job_id,
            job_type=job_type,
            config_s3_path=final_config_s3_path,
            dataset_name=dataset_name,
            user_id=user_id,
            config_type=config_type,
            description=description,
            recreate_connections=recreate_connections,
        )
        return job_id

    def _persist_job(
        self,
        job_id: str,
        job_type: str,
        config_s3_path: str,
        dataset_name: str,
        user_id: str,
        config_type: str,
        description: str,
        recreate_connections: bool,
    ) -> None:
        """Persist job to DynamoDB and enqueue to SQS."""
        job_metadata = self._create_job_metadata(
            job_id=job_id,
            job_type=job_type,
            config_s3_path=config_s3_path,
            dataset_name=dataset_name,
            user_id=user_id,
            config_type=config_type,
            description=description,
            recreate_connections=recreate_connections,
        )
        self.dynamodb_client.create_job(job_metadata)

        message = self._create_sqs_message(
            job_id=job_id,
            job_type=job_type,
            config_s3_path=config_s3_path,
            dataset_name=dataset_name,
            user_id=user_id,
            config_type=config_type,
            recreate_connections=recreate_connections,
        )
        self.sqs_client.send_message(
            message=message,
            message_group_id=dataset_name,
        )

    def _generate_job_id(self) -> str:
        """
        Generate unique job ID.

        Returns:
            UUID v4 as string
        """
        return str(uuid.uuid4())

    def _resolve_config_path(
        self, job_id: str, job_type: str, config_content: str | None, config_s3_path: str | None
    ) -> str:
        """
        Resolve config S3 path from inline content or reference.

        Supports three config sources:
        1. Inline config → Upload to czbillgen/api-jobs/{job_type}/{job_id}/
        2. Standard config reference → czbillgen/configs/{stage}/...
        3. Custom pre-uploaded config → czbillgen/api-jobs/...

        Args:
            job_id: Job identifier for S3 key generation
            job_type: Job type for S3 key organization
            config_content: Optional inline config content
            config_s3_path: Optional S3 path reference

        Returns:
            Final S3 path to config

        Raises:
            ValueError: If neither or both configs provided, or invalid path
        """
        if config_content and config_s3_path:
            raise ValueError("Provide either config_content or config_s3_path, not both")

        if not config_content and not config_s3_path:
            raise ValueError("Either config_content or config_s3_path required")

        if config_content:
            # Upload inline config to job-specific path with job_type prefix
            s3_key = f"czbillgen/api-jobs/{job_type}/{job_id}/config.yaml"
            return self.s3_client.put_object(s3_key, config_content)

        # Validate config_s3_path prefix
        valid_prefixes = ["czbillgen/configs/", "czbillgen/api-jobs/"]
        if not any(config_s3_path.startswith(prefix) for prefix in valid_prefixes):
            raise ValueError(
                f"Invalid config path: {config_s3_path}\n"
                f"Config path must start with one of: {', '.join(valid_prefixes)}\n"
                f"Examples:\n"
                f"  - czbillgen/configs/k8s/production/config.k8s.yaml (standard config)\n"
                f"  - czbillgen/api-jobs/my-custom/config.yaml (custom config)"
            )

        return config_s3_path

    def _create_job_metadata(
        self,
        job_id: str,
        job_type: str,
        config_s3_path: str,
        dataset_name: str,
        user_id: str,
        config_type: str,
        description: str,
        recreate_connections: bool = False,
    ) -> dict[str, Any]:
        """
        Create job metadata dict for DynamoDB.

        Args:
            job_id: Unique job identifier
            job_type: Type of job
            config_s3_path: S3 path to config
            dataset_name: Dataset identifier
            user_id: User identifier
            config_type: 'file' or 'directory'
            description: Job description
            recreate_connections: Recreate connections flag

        Returns:
            Job metadata dict
        """
        metadata = {
            "job_id": job_id,
            "job_type": job_type,
            "dataset_name": dataset_name,
            "user_id": user_id,
            "status": "queued",
            "created_at": datetime.utcnow().isoformat(),
            "config_s3_path": config_s3_path,
            "config_type": config_type,
            "description": description,
        }
        if recreate_connections:
            metadata["recreate_connections"] = True
        return metadata

    def _create_sqs_message(
        self,
        job_id: str,
        job_type: str,
        config_s3_path: str,
        dataset_name: str,
        user_id: str,
        config_type: str,
        recreate_connections: bool = False,
    ) -> dict[str, Any]:
        """
        Create SQS message payload.

        Args:
            job_id: Job identifier
            job_type: Type of job
            config_s3_path: S3 path to config
            dataset_name: Dataset identifier
            user_id: User identifier
            config_type: 'file' or 'directory'
            recreate_connections: Recreate connections flag

        Returns:
            SQS message dict
        """
        message = {
            "job_id": job_id,
            "job_type": job_type,
            "config_s3_path": config_s3_path,
            "dataset_name": dataset_name,
            "user_id": user_id,
            "config_type": config_type,
            "submitted_at": datetime.utcnow().isoformat(),
        }
        if recreate_connections:
            message["recreate_connections"] = True
        return message
