"""Infrastructure configuration specification.

This module defines infrastructure-related configuration for
Prism-generated applications, including reverse proxy, SSL settings,
and Hetzner Cloud worker infrastructure.
"""

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field


class HetznerWorkerJobSpec(BaseModel):
    """Specification for a single background worker job on Hetzner.

    Defines a long-running or scheduled background job that runs on
    a Hetzner Cloud VM, similar to a Kubernetes Job or CronJob.

    Example:
        ```python
        from prisme.spec.infrastructure import HetznerWorkerJobSpec

        job = HetznerWorkerJobSpec(
            name="price-fetcher",
            schedule="*/15 * * * *",
            command="python -m app.jobs.fetch_prices",
            cpu=2,
            memory_mb=2048,
        )
        ```
    """

    name: str = Field(
        ...,
        description="Unique job name (kebab-case, e.g. 'price-fetcher')",
    )
    command: str = Field(
        ...,
        description="Command to execute (e.g. 'python -m app.jobs.fetch_prices')",
    )
    schedule: str | None = Field(
        default=None,
        description="Cron expression for scheduled jobs (e.g. '*/15 * * * *'). "
        "If None, the job runs continuously as a long-running worker.",
    )
    server_type: str = Field(
        default="cx22",
        description="Hetzner server type (e.g. cx22, cx32, cax11)",
    )
    location: str = Field(
        default="fsn1",
        description="Hetzner datacenter location",
    )
    image: str = Field(
        default="ubuntu-24.04",
        description="Hetzner OS image",
    )
    cpu: int = Field(
        default=2,
        description="Number of vCPUs (informational, maps to server_type selection)",
    )
    memory_mb: int = Field(
        default=4096,
        description="Memory in MB (informational, maps to server_type selection)",
    )
    restart_policy: Literal["always", "on-failure", "never"] = Field(
        default="on-failure",
        description="Restart policy: 'always' (long-running), 'on-failure' (retry on crash), "
        "'never' (run once then destroy)",
    )
    max_restarts: int = Field(
        default=3,
        description="Maximum restart attempts before giving up (only for 'on-failure' policy)",
    )
    timeout_seconds: int = Field(
        default=3600,
        description="Maximum execution time in seconds before the job is killed",
    )
    environment: dict[str, str] = Field(
        default_factory=dict,
        description="Additional environment variables for the job",
    )
    docker_image: str | None = Field(
        default=None,
        description="Docker image to run. If None, uses the project's backend image.",
    )
    labels: dict[str, str] = Field(
        default_factory=lambda: {"managed-by": "prisme", "purpose": "worker"},
        description="Labels applied to the worker VM",
    )

    model_config = {"extra": "forbid"}


class HetznerWorkersConfig(BaseModel):
    """Configuration for Hetzner Cloud background workers.

    Defines a set of background worker jobs that run on dedicated
    Hetzner Cloud VMs. Each job gets its own VM (or shares one based
    on resource requirements).

    Example:
        ```python
        from prisme.spec.infrastructure import HetznerWorkersConfig, HetznerWorkerJobSpec

        workers = HetznerWorkersConfig(
            enabled=True,
            jobs=[
                HetznerWorkerJobSpec(
                    name="price-fetcher",
                    schedule="*/15 * * * *",
                    command="python -m app.jobs.fetch_prices",
                ),
                HetznerWorkerJobSpec(
                    name="report-generator",
                    command="python -m app.workers.reports",
                    restart_policy="always",
                ),
            ],
        )
        ```
    """

    enabled: bool = Field(
        default=False,
        description="Enable Hetzner Cloud worker infrastructure",
    )
    default_server_type: str = Field(
        default="cx22",
        description="Default Hetzner server type for worker VMs",
    )
    default_location: str = Field(
        default="fsn1",
        description="Default Hetzner datacenter location for workers",
    )
    ssh_key_name: str | None = Field(
        default=None,
        description="Hetzner SSH key name to inject into worker VMs",
    )
    docker_registry: str = Field(
        default="ghcr.io",
        description="Docker registry for pulling job images",
    )
    jobs: list[HetznerWorkerJobSpec] = Field(
        default_factory=list,
        description="List of background worker job specifications",
    )

    model_config = {"extra": "forbid"}


class AWSBatchJobSpec(BaseModel):
    """Specification for a single AWS Batch job.

    Defines a batch or scheduled job that runs on AWS Batch,
    similar to a Kubernetes Job with CPU/memory resource requests.

    Example:
        ```python
        from prisme.spec.infrastructure import AWSBatchJobSpec

        job = AWSBatchJobSpec(
            name="data-etl",
            command=["python", "-m", "app.jobs.etl"],
            vcpus=2,
            memory_mb=4096,
            schedule="rate(1 hour)",
        )
        ```
    """

    name: str = Field(
        ...,
        description="Unique job name (kebab-case)",
    )
    command: list[str] = Field(
        ...,
        description="Command to execute as a list (e.g. ['python', '-m', 'app.jobs.run'])",
    )
    vcpus: int = Field(
        default=1,
        description="Number of vCPUs for the job",
    )
    memory_mb: int = Field(
        default=2048,
        description="Memory in MB for the job",
    )
    schedule: str | None = Field(
        default=None,
        description="EventBridge schedule expression (e.g. 'rate(1 hour)', 'cron(0 12 * * ? *)'). "
        "If None, the job must be triggered manually or via API.",
    )
    timeout_seconds: int = Field(
        default=3600,
        description="Maximum execution time in seconds",
    )
    retry_attempts: int = Field(
        default=1,
        description="Number of retry attempts on failure",
    )
    docker_image: str | None = Field(
        default=None,
        description="Docker image to run. If None, uses the project's backend image.",
    )
    environment: dict[str, str] = Field(
        default_factory=dict,
        description="Environment variables for the job",
    )
    platform: Literal["FARGATE", "EC2"] = Field(
        default="FARGATE",
        description="AWS Batch compute platform: FARGATE (serverless) or EC2",
    )

    model_config = {"extra": "forbid"}


class AWSBatchConfig(BaseModel):
    """AWS Batch configuration for background/batch job processing.

    Example:
        ```python
        from prisme.spec.infrastructure import AWSBatchConfig, AWSBatchJobSpec

        batch = AWSBatchConfig(
            enabled=True,
            region="eu-west-1",
            jobs=[
                AWSBatchJobSpec(
                    name="data-etl",
                    command=["python", "-m", "app.jobs.etl"],
                    schedule="rate(1 hour)",
                ),
            ],
        )
        ```
    """

    enabled: bool = Field(
        default=False,
        description="Enable AWS Batch infrastructure",
    )
    region: str = Field(
        default="eu-west-1",
        description="AWS region for Batch resources",
    )
    compute_environment_name: str | None = Field(
        default=None,
        description="Compute environment name (defaults to {project}-batch-env)",
    )
    job_queue_name: str | None = Field(
        default=None,
        description="Job queue name (defaults to {project}-job-queue)",
    )
    max_vcpus: int = Field(
        default=16,
        description="Maximum vCPUs for the compute environment",
    )
    subnets: list[str] = Field(
        default_factory=list,
        description="VPC subnet IDs for Batch compute (leave empty for default VPC)",
    )
    security_groups: list[str] = Field(
        default_factory=list,
        description="Security group IDs (leave empty for default)",
    )
    jobs: list[AWSBatchJobSpec] = Field(
        default_factory=list,
        description="List of batch job definitions",
    )

    model_config = {"extra": "forbid"}


class DockerBuildServiceConfig(BaseModel):
    """Docker image build service configuration.

    Defines where and how Docker images are built and pushed.
    Supports both AWS ECR and Hetzner-based build pipelines.

    Example:
        ```python
        from prisme.spec.infrastructure import DockerBuildServiceConfig

        build = DockerBuildServiceConfig(
            provider="hetzner",
            registry="ghcr.io",
            build_server_type="cx32",
        )
        ```
    """

    provider: Literal["github", "hetzner", "aws"] = Field(
        default="github",
        description="Build provider: 'github' (GitHub Actions), "
        "'hetzner' (build on Hetzner VM), 'aws' (build with CodeBuild + ECR)",
    )
    registry: str = Field(
        default="ghcr.io",
        description="Docker registry (e.g. ghcr.io, <account>.dkr.ecr.<region>.amazonaws.com)",
    )
    # Hetzner-specific build settings
    build_server_type: str = Field(
        default="cx32",
        description="Hetzner server type for build VM (larger = faster builds)",
    )
    build_location: str = Field(
        default="fsn1",
        description="Hetzner datacenter for build VM",
    )
    # AWS-specific build settings
    aws_region: str = Field(
        default="eu-west-1",
        description="AWS region for ECR and CodeBuild",
    )
    ecr_repository: str | None = Field(
        default=None,
        description="ECR repository name (defaults to project name)",
    )
    # Common settings
    build_args: dict[str, str] = Field(
        default_factory=dict,
        description="Docker build arguments",
    )
    platforms: list[str] = Field(
        default_factory=lambda: ["linux/amd64"],
        description="Target platforms for multi-arch builds (e.g. linux/amd64, linux/arm64)",
    )
    cache_from: str | None = Field(
        default=None,
        description="Docker cache source (e.g. type=registry,ref=...)",
    )

    model_config = {"extra": "forbid"}


class TraefikConfig(BaseModel):
    """Traefik reverse proxy configuration.

    Controls the Traefik deployment for routing and SSL termination.

    Example:
        ```python
        from prisme.spec import TraefikConfig

        traefik = TraefikConfig(
            enabled=True,
            ssl_provider="letsencrypt",
            ssl_email="admin@example.com",
            domain="example.com",
        )
        ```
    """

    enabled: bool = Field(
        default=False,
        description="Enable Traefik reverse proxy",
    )
    ssl_provider: Literal["letsencrypt", "manual", "none"] = Field(
        default="letsencrypt",
        description="SSL certificate provider: letsencrypt (automatic), manual (provide certs), none (no SSL)",
    )
    ssl_email: str = Field(
        default="${SSL_EMAIL}",
        description="Email for Let's Encrypt certificate notifications",
    )
    domain: str = Field(
        default="${DOMAIN}",
        description="Primary domain for the application",
    )
    dashboard_enabled: bool = Field(
        default=False,
        description="Enable Traefik dashboard (not recommended in production)",
    )
    dashboard_subdomain: str = Field(
        default="traefik",
        description="Subdomain for Traefik dashboard (e.g., traefik.{domain})",
    )

    model_config = {"extra": "forbid"}
