from dataclasses import dataclass
from typing import Any, List, Literal, cast
from datetime import datetime, date, time, timedelta
from decimal import Decimal
from typing import Type, Optional
from pydantic import BaseModel


Engines = Literal["duckdb", "polars"]


FileTypes = Literal[
    "delta",
    "parquet",
    "arrow",
    "arrow-stream",
    "avro",
    "csv",
    "json",
    "ndjson",
]
OutputFileType = Literal[
    "json",
    "ndjson",
    "parquet",
    "csv",
    "csv4excel",
    "xlsx",
    "feather",
    "html",
    "scsv",
    "xml",
    "ipc",
    "arrow",
    "arrow-stream",
]
OperatorType = Literal[
    "<",
    "=",
    ">",
    ">=",
    "<=",
    "<>",
    "contains",
    "in",
    "not contains",
    "not in",
    "not null",
    "null",
    "between",
    "not between",
]
DeltaOperatorTypes = Literal["<", "=", ">", ">=", "<=", "in", "not in"]
PolaryTypeFunction = Literal[
    "sum",
    "count",
    "mean",
    "median",
    "min",
    "max",
    "first",
    "std",
    "n_unique",
    "distinct",
    "mode",
    "null_count",
]

CONFIG_DTYPE_MAP: dict[str, Type] = {
    "struct": dict,
    "string": str,
    "integer": int,
    "float": float,
    "array": list,
    "double": Decimal,
    "timesamp": datetime,
    "date": date,
    "time": time,
    "duration": timedelta,
    "str": str,
    "int": int,
    "bool": bool,
    "boolean": bool,
}


DTYPE_MAP = {
    "struct": dict,
    "string": str,
    "integer": int,
    "float": float,
    "array": list,
    "double": Decimal,
    "timesamp": datetime,
    "date": date,
    "time": time,
}


class MetadataSchemaFieldType(BaseModel):
    type_str: str
    orig_type_str: str
    fields: "Optional[list[MetadataSchemaField]]"
    inner: "Optional[MetadataSchemaFieldType]"


class MetadataSchemaField(BaseModel):
    name: str
    type: MetadataSchemaFieldType
    max_str_length: Optional[int] = None


@dataclass
class SearchConfig:
    name: str
    columns: List[str]


@dataclass
class Param:
    name: str
    combi: Optional[Optional[List[str]]] = None
    default: Optional[str] = None
    required: Optional[bool] = False
    operators: Optional[list[OperatorType]] = None
    colname: Optional[str] = None

    @property
    def real_default(self) -> str:
        import ast

        self._real_default = ast.literal_eval(self.default) if self.default else None
        return cast(str, self._real_default)


class MetadataDetailResult(BaseModel):
    partition_values: Optional[list[dict[str, Any]]]
    partition_columns: List[str]
    max_string_lengths: dict[str, Optional[int]]
    data_schema: list[MetadataSchemaField]
    delta_meta: Optional[dict]
    delta_schema: Any
    parameters: Optional[List[Any]]
    search: Optional[List[Any]]


MetadataSchemaFieldType.update_forward_refs()
MetadataSchemaField.update_forward_refs()
MetadataDetailResult.update_forward_refs()
