"""Query parameter and response models."""

from datetime import datetime
from enum import Enum
from uuid import UUID

from pydantic import BaseModel, Field, field_validator

from fishinfo.models.telemetry import DataType, TelemetryPoint


class OrderDirection(str, Enum):
    """Order direction for query results."""

    ASC = "asc"
    DESC = "desc"


class AggregationType(str, Enum):
    """Aggregation type for time-bucketed queries."""

    AVG = "avg"
    MIN = "min"
    MAX = "max"
    COUNT = "count"
    SUM = "sum"
    FIRST = "first"
    LAST = "last"


class PaginationMeta(BaseModel):
    """Pagination metadata.

    Attributes:
        total: Total number of results
        limit: Limit used in query
        offset: Offset used in query
        has_more: Whether there are more results
    """

    total: int = Field(..., description="Total number of results")
    limit: int = Field(..., description="Limit used in query")
    offset: int = Field(..., description="Offset used in query")
    has_more: bool = Field(..., description="Whether there are more results")


class TelemetryResponse(BaseModel):
    """Response containing a list of telemetry points with pagination.

    Attributes:
        data: List of telemetry points
        meta: Pagination metadata
    """

    data: list[TelemetryPoint] = Field(..., description="List of telemetry points")
    meta: PaginationMeta = Field(..., description="Pagination metadata")


class LatestTelemetryResponse(BaseModel):
    """Response containing the latest telemetry point(s) for a vessel.

    Attributes:
        vessel_id: Vessel ID
        data: Latest telemetry point(s), one per data_type if filtered
    """

    vessel_id: UUID = Field(..., description="Vessel ID")
    data: list[TelemetryPoint] = Field(
        ...,
        description="Latest telemetry point(s), one per data_type if filtered",
    )


class AggregatedDataPoint(BaseModel):
    """A single aggregated data point.

    Attributes:
        bucket: Start of the time bucket
        value: Aggregated value
        count: Number of points in bucket (included for context)
    """

    bucket: datetime = Field(..., description="Start of the time bucket")
    value: float | None = Field(..., description="Aggregated value")
    count: int | None = Field(
        default=None,
        description="Number of points in bucket (included for context)",
    )


class AggregatedTelemetry(BaseModel):
    """Response containing aggregated telemetry data.

    Attributes:
        vessel_id: Vessel ID
        data_type: Data type
        interval: Time bucket interval
        agg_type: Aggregation type used
        start_time: Query start time
        end_time: Query end time
        data: Aggregated data points
    """

    vessel_id: UUID = Field(..., description="Vessel ID")
    data_type: DataType = Field(..., description="Data type")
    interval: str = Field(..., description="Time bucket interval")
    agg_type: AggregationType = Field(..., description="Aggregation type used")
    start_time: datetime = Field(..., description="Query start time")
    end_time: datetime = Field(..., description="Query end time")
    data: list[AggregatedDataPoint] = Field(
        ...,
        description="Aggregated data points",
    )


class TelemetryQueryParams(BaseModel):
    """Parameters for querying telemetry data.

    Attributes:
        vessel_id: Vessel ID to query
        data_type: Data type filter (comma-separated for multiple types)
        start_time: Start time for query range (inclusive)
        end_time: End time for query range (exclusive)
        limit: Maximum number of results (default: 1000, max: 10000)
        offset: Offset for pagination
        order: Sort order by time (asc or desc)
    """

    vessel_id: UUID = Field(..., description="Vessel ID to query")
    data_type: str | None = Field(
        default=None,
        description="Data type filter (comma-separated for multiple types)",
    )
    start_time: datetime | None = Field(
        default=None,
        description="Start time for query range (inclusive)",
    )
    end_time: datetime | None = Field(
        default=None,
        description="End time for query range (exclusive)",
    )
    limit: int = Field(
        default=1000,
        ge=1,
        le=10000,
        description="Maximum number of results (default: 1000, max: 10000)",
    )
    offset: int = Field(
        default=0,
        ge=0,
        description="Offset for pagination",
    )
    order: OrderDirection = Field(
        default=OrderDirection.DESC,
        description="Sort order by time (asc or desc)",
    )

    @field_validator("data_type")
    @classmethod
    def validate_data_types(cls, v: str | None) -> str | None:
        """Validate comma-separated data types."""
        if v is None:
            return None

        # Split and validate each type
        types = [t.strip() for t in v.split(",")]
        valid_types = {dt.value for dt in DataType}

        for data_type in types:
            if data_type not in valid_types:
                raise ValueError(
                    f"Invalid data_type '{data_type}'. "
                    f"Valid types: {', '.join(sorted(valid_types))}"
                )

        return v


class AggregationParams(BaseModel):
    """Parameters for aggregated telemetry queries.

    Attributes:
        vessel_id: Vessel ID to query
        data_type: Data type to aggregate
        start_time: Start time for aggregation range
        end_time: End time for aggregation range
        interval: Time bucket interval (e.g., '1 minute', '5 minutes', '1 hour', '1 day')
        agg_type: Aggregation function to apply
    """

    vessel_id: UUID = Field(..., description="Vessel ID to query")
    data_type: DataType = Field(..., description="Data type to aggregate")
    start_time: datetime = Field(..., description="Start time for aggregation range")
    end_time: datetime = Field(..., description="End time for aggregation range")
    interval: str = Field(
        ...,
        description="Time bucket interval (e.g., '1 minute', '5 minutes', '1 hour', '1 day')",
    )
    agg_type: AggregationType = Field(
        default=AggregationType.AVG,
        description="Aggregation function to apply",
    )

    @field_validator("interval")
    @classmethod
    def validate_interval(cls, v: str) -> str:
        """Validate TimescaleDB interval format."""
        parts = v.strip().split()
        if len(parts) != 2:
            raise ValueError(
                "Interval must be in format '<number> <unit>' "
                "(e.g., '1 minute', '5 minutes', '1 hour')"
            )

        try:
            int(parts[0])
        except ValueError as exc:
            raise ValueError("Interval number must be an integer") from exc

        valid_units = {
            "second",
            "seconds",
            "minute",
            "minutes",
            "hour",
            "hours",
            "day",
            "days",
            "week",
            "weeks",
            "month",
            "months",
        }

        if parts[1].lower() not in valid_units:
            raise ValueError(
                f"Invalid interval unit '{parts[1]}'. "
                f"Valid units: {', '.join(sorted(valid_units))}"
            )

        return v


class DownsampleParams(BaseModel):
    """Parameters for downsampling telemetry data.

    Attributes:
        vessel_id: Vessel ID to query
        data_type: Data type to downsample
        start_time: Start time for query range
        end_time: End time for query range
        target_points: Target number of data points to return (default: 500)
    """

    vessel_id: UUID = Field(..., description="Vessel ID to query")
    data_type: DataType = Field(..., description="Data type to downsample")
    start_time: datetime = Field(..., description="Start time for query range")
    end_time: datetime = Field(..., description="End time for query range")
    target_points: int = Field(
        default=500,
        ge=10,
        le=10000,
        description="Target number of data points to return (default: 500)",
    )
