"""
Dataset domain model.

Represents a dataset entity with schema, profile, and quality metrics.
"""

from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any
from uuid import UUID, uuid4

from pydantic import BaseModel, Field, computed_field

from app.domain.models.ir import DatasetSchema


class DatasetStatus(str, Enum):
    """Dataset processing status."""

    UPLOADING = "uploading"
    UPLOADED = "uploaded"
    PROFILING = "profiling"
    READY = "ready"
    PROCESSING = "processing"
    FAILED = "failed"
    ARCHIVED = "archived"


class DataQualityMetrics(BaseModel):
    """Data quality metrics for a dataset."""

    total_rows: int
    total_columns: int
    missing_values: dict[str, int] = Field(default_factory=dict)
    missing_percentage: dict[str, float] = Field(default_factory=dict)
    duplicate_rows: int = 0
    unique_values: dict[str, int] = Field(default_factory=dict)
    column_types: dict[str, str] = Field(default_factory=dict)
    memory_usage_mb: float = 0.0

    @computed_field  # type: ignore[misc]
    @property
    def overall_missing_percentage(self) -> float:
        """Calculate overall missing percentage."""
        if not self.missing_percentage:
            return 0.0
        return sum(self.missing_percentage.values()) / len(self.missing_percentage)

    @computed_field  # type: ignore[misc]
    @property
    def has_quality_issues(self) -> bool:
        """Check if dataset has quality issues."""
        return (
            self.overall_missing_percentage > 10.0
            or self.duplicate_rows > 0
            or any(pct > 50.0 for pct in self.missing_percentage.values())
        )


class ColumnProfile(BaseModel):
    """Profile for a single column."""

    name: str
    dtype: str
    count: int
    null_count: int
    null_percentage: float
    unique_count: int
    
    # Numeric stats
    min_value: float | int | None = None
    max_value: float | int | None = None
    mean: float | None = None
    median: float | None = None
    std: float | None = None
    
    # Categorical stats
    most_common: list[tuple[Any, int]] | None = None
    
    # Anomalies
    outlier_count: int = 0
    anomalies: list[str] = Field(default_factory=list)


class DatasetProfile(BaseModel):
    """Complete profile of a dataset."""

    column_profiles: list[ColumnProfile] = Field(default_factory=list)
    correlations: dict[str, dict[str, float]] = Field(default_factory=dict)
    generated_at: datetime = Field(default_factory=datetime.utcnow)

    def get_column_profile(self, name: str) -> ColumnProfile | None:
        """Get profile for a specific column."""
        for profile in self.column_profiles:
            if profile.name == name:
                return profile
        return None


class Dataset(BaseModel):
    """
    Dataset entity.

    Represents an uploaded dataset with metadata, schema, and quality information.
    """

    id: UUID = Field(default_factory=uuid4)
    name: str
    original_filename: str
    file_path: Path
    file_format: str  # csv, xlsx, json, parquet
    file_size_bytes: int
    status: DatasetStatus = DatasetStatus.UPLOADING
    
    # Schema and structure
    schema: DatasetSchema | None = None
    row_count: int = 0
    column_count: int = 0
    
    # Quality and profiling
    quality_metrics: DataQualityMetrics | None = None
    profile: DatasetProfile | None = None
    
    # Metadata
    uploaded_at: datetime = Field(default_factory=datetime.utcnow)
    updated_at: datetime = Field(default_factory=datetime.utcnow)
    uploaded_by: str = "system"
    tags: list[str] = Field(default_factory=list)
    description: str = ""

    @computed_field  # type: ignore[misc]
    @property
    def file_size_mb(self) -> float:
        """Get file size in MB."""
        return self.file_size_bytes / (1024 * 1024)

    @computed_field  # type: ignore[misc]
    @property
    def is_ready(self) -> bool:
        """Check if dataset is ready for processing."""
        return self.status == DatasetStatus.READY

    @computed_field  # type: ignore[misc]
    @property
    def has_schema(self) -> bool:
        """Check if dataset has schema defined."""
        return self.schema is not None

    @computed_field  # type: ignore[misc]
    @property
    def has_profile(self) -> bool:
        """Check if dataset has been profiled."""
        return self.profile is not None

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return self.model_dump(mode="json")

    def to_summary(self) -> dict[str, Any]:
        """Convert to summary dict (exclude large fields)."""
        data = self.to_dict()
        # Remove large fields
        if "profile" in data:
            data.pop("profile")
        return data
