"""Configuration models for estat_api_dlt_helper."""

from typing import Any, Dict, List, Literal, Optional, Union

from pydantic import BaseModel, ConfigDict, Field, field_validator


class SourceConfig(BaseModel):
    """Configuration for e-Stat API data source.

    Defines parameters for fetching statistical data from e-Stat API,
    including authentication, data selection, and pagination options.

    Attributes:
        app_id: e-Stat API application ID for authentication.
        statsDataId: Statistical table ID(s) to fetch.
        lang: Language for API response (J: Japanese, E: English).
        metaGetFlg: Whether to fetch metadata.
        cntGetFlg: Whether to fetch only record count.
    """

    app_id: str = Field(..., description="e-Stat API application ID (API key)")
    statsDataId: Union[str, List[str]] = Field(
        ...,
        description="Statistical table ID(s) to fetch. Can be a single ID or list of IDs",
    )
    lang: Literal["J", "E"] = Field(default="J", description="Language of the API response")
    metaGetFlg: Literal["Y", "N"] = Field(
        default="Y", description="Whether to fetch metadata (Y/N)"
    )
    cntGetFlg: Literal["Y", "N"] = Field(
        default="N", description="Whether to fetch only count (Y/N)"
    )

    # 以下オプションパラメータ
    explanationGetFlg: Optional[Literal["Y", "N"]] = Field(
        default="Y", description="Whether to fetch explanations (Y/N)"
    )
    annotationGetFlg: Optional[Literal["Y", "N"]] = Field(
        default="Y", description="Whether to fetch annotations (Y/N)"
    )
    replaceSpChars: Literal["0", "1", "2", "3"] = Field(
        default="2",
        description="Special character replacement mode | 0: 置換しない, 1: 置換する, 2: NULL, 3: 'NA'",
    )

    # データ選択パラメータ
    lvTab: Optional[str] = Field(default=None, description="Table level")
    cdTab: Optional[str] = Field(default=None, description="Table code")
    cdTabFrom: Optional[str] = Field(default=None, description="Table code from")
    cdTabTo: Optional[str] = Field(default=None, description="Table code to")
    lvTime: Optional[str] = Field(default=None, description="Time level")
    cdTime: Optional[str] = Field(default=None, description="Time code")
    cdTimeFrom: Optional[str] = Field(default=None, description="Time code from")
    cdTimeTo: Optional[str] = Field(default=None, description="Time code to")
    lvArea: Optional[str] = Field(default=None, description="Area level")
    cdArea: Optional[str] = Field(default=None, description="Area code")
    cdAreaFrom: Optional[str] = Field(default=None, description="Area code from")
    cdAreaTo: Optional[str] = Field(default=None, description="Area code to")
    # ... see https://api.e-stat.go.jp/swagger-ui/e-statapi3.0.html#/

    # ページネーションパラメータ
    limit: int = Field(
        default=100000, description="Maximum number of records to fetch per request"
    )
    maximum_offset: Optional[int] = Field(
        default=None, description="Maximum number of records to fetch"
    )

    # 分類事項パラメータ（cat01-cat15）
    # 動的に生成されるため、Extraで受け入れる
    model_config = ConfigDict(extra="allow")

    @field_validator("statsDataId")
    @classmethod
    def validate_stats_data_id(cls, v: Union[str, List[str]]) -> Union[str, List[str]]:
        """Ensure statsDataId is valid."""
        if isinstance(v, list):
            if not v:
                raise ValueError("statsDataId list cannot be empty")
            for id_ in v:
                if not isinstance(id_, str) or not id_.strip():
                    raise ValueError(f"Invalid statsDataId: {id_}")
        elif isinstance(v, str):
            if not v.strip():
                raise ValueError("statsDataId cannot be empty")
        return v


class DestinationConfig(BaseModel):
    """Configuration for DLT data destination.

    Defines parameters for loading data to various destinations using DLT,
    including destination type, dataset/table names, and write strategies.

    Attributes:
        destination: DLT destination type or configuration object.
        dataset_name: Target dataset/schema name.
        table_name: Target table name.
        write_disposition: How to write data (append/replace/merge).
        primary_key: Primary key columns for merge operations.
    """

    destination: Union[str, Any] = Field(
        ...,
        description="DLT destination configuration | e.g. 'bigquery', 'duckdb', 'filesystem', 'motherduck'",
    )
    dataset_name: str = Field(..., description="Dataset/schema name in the destination")
    table_name: str = Field(..., description="Table name in the destination")
    write_disposition: Literal["append", "replace", "merge"] = Field(
        default="merge", description="How to write data to the destination table"
    )
    primary_key: Optional[Union[str, List[str]]] = Field(
        default=["time", "area", "cat01"],
        description="Primary key column(s) for merge operations",
    )

    # DLT pipeline configuration
    pipeline_name: Optional[str] = Field(default=None, description="Name of the DLT pipeline")
    dev_mode: bool = Field(default=False, description="Enable DLT development mode")

    # Additional destination-specific configuration
    credentials: Optional[Dict[str, Any]] = Field(
        default=None, description="Destination-specific credentials"
    )
    extra_options: Optional[Dict[str, Any]] = Field(
        default=None, description="Additional destination-specific options"
    )

    @field_validator("primary_key")
    @classmethod
    def validate_primary_key(
        cls, v: Optional[Union[str, List[str]]], info
    ) -> Optional[Union[str, List[str]]]:
        """Validate primary key is provided for merge operations."""
        write_disposition = info.data.get("write_disposition")
        if write_disposition == "merge" and not v:
            raise ValueError(
                "primary_key must be specified when write_disposition is 'merge'"
            )
        return v


class EstatDltConfig(BaseModel):
    """Main configuration for e-Stat API to DLT integration.

    Combines source and destination configurations with additional
    processing options for data extraction and loading.

    Attributes:
        source: e-Stat API source configuration.
        destination: DLT destination configuration.
        batch_size: Number of records per batch.
        max_retries: Maximum API retry attempts.
        timeout: API request timeout in seconds.
    """

    source: SourceConfig = Field(..., description="e-Stat API source configuration")
    destination: DestinationConfig = Field(
        ..., description="DLT destination configuration"
    )

    # Optional processing configuration
    batch_size: Optional[int] = Field(
        default=None, description="Number of records to process in each batch"
    )
    max_retries: int = Field(default=3, description="Maximum number of API retry attempts")
    timeout: Optional[int] = Field(default=None, description="API request timeout in seconds")

    # Data transformation options
    flatten_metadata: bool = Field(
        default=False, description="Whether to flatten metadata into table columns"
    )
    include_api_metadata: bool = Field(
        default=True, description="Whether to include API response metadata in the table"
    )

    model_config = ConfigDict(
        validate_assignment=True,
        extra="forbid",
    )
