"""Post-processing response schemas."""

from datetime import datetime
from enum import StrEnum
from typing import Any
from uuid import UUID
from .common import BaseResponse, Field
from ..enums import FindingType


class PostProcessingJobStatus(StrEnum):
    queued = "queued"
    running = "running"
    succeeded = "succeeded"
    failed = "failed"


class PostProcessingJobResponse(BaseResponse):
    job_id: UUID = Field(..., description="ID of the background post-processing job")
    resource_type: str = Field(..., description="Type of resource being processed")
    resource_id: UUID = Field(..., description="ID of the target resource")
    alignment_id: UUID | None = Field(
        default=None,
        description="Owning alignment ID when the job targets a bucket",
    )
    operation: str = Field(..., description="Post-processing operation name")
    status: PostProcessingJobStatus = Field(..., description="Current job status")
    stage: str | None = Field(default=None, description="Current execution stage")
    celery_task_id: str | None = Field(
        default=None,
        description="Underlying Celery task ID, when dispatched",
    )
    result_payload: dict[str, Any] | None = Field(
        default=None,
        description="Job-specific result payload when the job succeeds",
    )
    error_message: str | None = Field(
        default=None,
        description="Terminal error message when the job fails",
    )
    error_kind: str | None = Field(
        default=None,
        description="Normalized terminal error kind when the job fails",
    )
    started_at: datetime | None = Field(
        default=None,
        description="When execution started",
    )
    finished_at: datetime | None = Field(
        default=None,
        description="When execution finished",
    )
    created_at: datetime = Field(..., description="When the job was created")
    updated_at: datetime = Field(..., description="When the job was last updated")


class MapFindingsRequest(BaseResponse):
    keys: list[str] = Field(
        description=(
            "List of finding key fields to use for mapping. If not provided, all "
            "available key fields will be used."
        ),
        min_length=1,
    )
    finding_type: FindingType | None = Field(
        default=None,
        description=(
            "Optional finding type filter to apply during mapping. When specified, "
            "only findings with a matching 'finding_classification.finding_type' "
            "will be processed."
        ),
    )
    filter_item_type_id: UUID | None = Field(
        default=None,
        description=(
            "Optional item type ID filter to apply during mapping. When specified, "
            "only findings with a matching 'item_type_version.item_type_id' will be "
            "processed."
        ),
    )


class AlignmentGraphExpandRequest(BaseResponse):
    work_type: str = Field(
        default="expand_dependencies",
        description="Work type to expand: expand_siblings or expand_dependencies.",
    )
    batch_size: int = Field(
        default=100,
        ge=1,
        le=1000,
        description="Maximum number of work items to claim in this expansion call.",
    )
    worker_id: str | None = Field(
        default=None,
        description="Optional worker identifier used for claim attribution.",
    )


class AlignmentGraphStatusResponse(BaseResponse):
    alignment_id: UUID = Field(..., description="ID of the alignment")
    total: int = Field(..., description="Total work items")
    pending: int = Field(..., description="Pending work items")
    queued: int = Field(..., description="Queued work items")
    retry_wait: int = Field(
        default=0,
        description="Pending work items that are cooling down before retry.",
    )
    in_progress: int = Field(..., description="In-progress work items")
    completed: int = Field(..., description="Completed work items")
    failed: int = Field(..., description="Failed work items")
    last_updated_at: datetime | None = Field(
        default=None,
        description="Most recent UTC timestamp when any graph work item changed",
    )


class AlignmentGraphOperationResponse(BaseResponse):
    alignment_id: UUID = Field(..., description="ID of the alignment")
    items_processed: int = Field(..., description="Number of items processed")


class SiblingKeyConfig(BaseResponse):
    """Per item-type/version sibling expansion key configuration."""

    item_type_id: UUID = Field(..., description="Item type ID")
    version: str | None = Field(
        default=None,
        description="Item type version. When None, the config acts as a wildcard and applies to all versions.",
    )
    keys: list[str] = Field(
        ...,
        min_length=1,
        description="Key fields used to query DSG Core for sibling items.",
    )


class AlignmentGraphStartProcessingRequest(BaseResponse):
    """Optional overrides for starting graph processing."""

    batch_size: int | None = Field(
        default=None,
        ge=1,
        le=1000,
        description="Optional batch size override for this run.",
    )
    timeout_seconds: int | None = Field(
        default=None,
        ge=60,
        le=86400,
        description="Optional timeout override for this run.",
    )
    sibling_keys: list[SiblingKeyConfig] | None = Field(
        default=None,
        description=(
            "Per item-type/version sibling expansion key configs. "
            "When provided, these override the service default keys for each "
            "matching item type (and version) during graph expansion."
        ),
    )


class AlignmentGraphStartProcessingResponse(BaseResponse):
    alignment_id: UUID = Field(..., description="ID of the alignment")
    job_id: str = Field(..., description="Celery job ID handling the graph build")


class BucketFindingsResponse(BaseResponse):
    """Response schema for bucket-scoped findings operations."""

    bucket_id: UUID = Field(..., description="ID of the bucket")
    findings_processed: int = Field(..., description="Number of findings processed")


class BucketCommandsResponse(BaseResponse):
    """Response schema for bucket-scoped commands operations."""

    bucket_id: UUID = Field(..., description="ID of the bucket")
    commands_processed: int = Field(..., description="Number of commands processed")


class AlignmentFindingsResponse(BaseResponse):
    """Response schema for alignment-scoped findings operations."""

    alignment_id: UUID = Field(..., description="ID of the alignment")
    findings_processed: int = Field(..., description="Number of findings processed")


class AlignmentCommandsResponse(BaseResponse):
    """Response schema for alignment-scoped commands operations."""

    alignment_id: UUID = Field(..., description="ID of the alignment")
    commands_processed: int = Field(..., description="Number of commands processed")


class BucketCommandsDropResponse(BaseResponse):
    """Response schema for bucket-scoped command cleanup operations."""

    bucket_id: UUID = Field(..., description="ID of the bucket")
    command_groups_deleted: int = Field(
        ..., description="Number of command groups deleted"
    )
    commands_deleted: int = Field(..., description="Number of commands deleted")
    command_dryruns_deleted: int = Field(
        ..., description="Number of command dry-run records deleted"
    )


class AlignmentCommandsDropResponse(BaseResponse):
    """Response schema for alignment-scoped command cleanup operations."""

    alignment_id: UUID = Field(..., description="ID of the alignment")
    buckets_processed: int = Field(..., description="Number of buckets evaluated")
    command_groups_deleted: int = Field(
        ..., description="Total number of command groups deleted"
    )
    commands_deleted: int = Field(..., description="Total number of commands deleted")
    command_dryruns_deleted: int = Field(
        ..., description="Total number of command dry-run records deleted"
    )


class AlignmentFindingsSummaryReportRequest(BaseResponse):
    """Request schema for findings summary report generation."""

    file_name: str | None = Field(
        default=None,
        description=(
            "Destination object key for the generated Excel file in bucket storage. "
            "When omitted, the API stores the report under "
            "reports/<alignment_name>[_from_<from_version>][_to_<to_version>]_findings_summary.xlsx."
        ),
    )
    sheet_name: str = Field(
        default="findings_summary",
        min_length=1,
        description="Sheet name used inside the generated workbook.",
    )


class AlignmentFindingsSummaryReportResponse(BaseResponse):
    """Response schema for findings summary report generation."""

    alignment_id: UUID = Field(..., description="ID of the alignment")
    output_path: str = Field(
        ..., description="Object key of the generated report in bucket storage"
    )
    sheet_name: str = Field(..., description="Workbook sheet name used for export")
    rows_exported: int = Field(..., description="Number of rows written to the report")
    download_url: str = Field(
        ..., description="Signed URL to download the generated report"
    )


class AlignmentArtifactsArchiveResponse(BaseResponse):
    """Response schema for alignment artifacts archive download URL."""

    alignment_id: UUID = Field(..., description="ID of the alignment")
    download_url: str = Field(
        ..., description="Signed URL to download artifacts archive"
    )
