"""
EIGIS Pydantic Models - Request/Response schemas
All models include GIS geometry support and validation
"""
from pydantic import BaseModel, Field, EmailStr, validator
from typing import Optional, List, Dict, Any
from datetime import datetime, date
from enum import Enum


# ============================================================
# ENUMS
# ============================================================
class ProjectStatus(str, Enum):
    planning = "planning"
    active = "active"
    paused = "paused"
    completed = "completed"
    cancelled = "cancelled"

class ObservationType(str, Enum):
    test_pit = "test_pit"
    gully = "gully"
    slope_cut = "slope_cut"
    river_valley = "river_valley"
    landslide = "landslide"
    quarry_face = "quarry_face"
    road_cut = "road_cut"
    natural_exposure = "natural_exposure"
    geothermal_spring = "geothermal_spring"
    fumarole = "fumarole"
    hot_spring = "hot_spring"
    geyser = "geyser"
    mineral_deposit = "mineral_deposit"
    other = "other"

class ObservationStatus(str, Enum):
    draft = "draft"
    saved = "saved"
    submitted = "submitted"
    reviewed = "reviewed"
    approved = "approved"

class HazardLevel(str, Enum):
    low = "low"
    moderate = "moderate"
    high = "high"
    very_high = "very_high"
    extreme = "extreme"

class ManifestationType(str, Enum):
    hot_spring = "hot_spring"
    warm_spring = "warm_spring"
    fumarole = "fumarole"
    steaming_ground = "steaming_ground"
    mud_pot = "mud_pot"
    geyser = "geyser"
    sinter_deposit = "sinter_deposit"
    travertine = "travertine"
    altered_ground = "altered_ground"
    hydrothermal_breccia = "hydrothermal_breccia"
    volcanic_vent = "volcanic_vent"
    mineral_spring = "mineral_spring"
    other = "other"

class SampleType(str, Enum):
    disturbed_soil = "disturbed_soil"
    undisturbed_soil = "undisturbed_soil"
    bulk_soil = "bulk_soil"
    rock_core = "rock_core"
    rock_chip = "rock_chip"
    water = "water"
    gas = "gas"
    geothermal_fluid = "geothermal_fluid"
    geothermal_gas = "geothermal_gas"
    alteration_mineral = "alteration_mineral"
    surface_sediment = "surface_sediment"
    vegetation = "vegetation"
    other = "other"


# ============================================================
# GEOMETRY MODELS
# ============================================================
class PointGeometry(BaseModel):
    """GeoJSON Point geometry for spatial data."""
    type: str = "Point"
    coordinates: List[float] = Field(..., min_length=2, max_length=3)
    # coordinates: [longitude, latitude] or [lon, lat, elevation]
    crs: Optional[Dict] = {"type": "name", "properties": {"name": "EPSG:4326"}}

class LineGeometry(BaseModel):
    """GeoJSON LineString geometry."""
    type: str = "LineString"
    coordinates: List[List[float]]
    crs: Optional[Dict] = {"type": "name", "properties": {"name": "EPSG:4326"}}

class PolygonGeometry(BaseModel):
    """GeoJSON Polygon geometry."""
    type: str = "Polygon"
    coordinates: List[List[List[float]]]
    crs: Optional[Dict] = {"type": "name", "properties": {"name": "EPSG:4326"}}


# ============================================================
# PROJECT MODELS
# ============================================================
class ProjectCreate(BaseModel):
    project_code: str = Field(..., max_length=50)
    project_name: str = Field(..., max_length=255)
    description: Optional[str] = None
    client_name: Optional[str] = None
    client_email: Optional[EmailStr] = None
    receptionist_email: Optional[EmailStr] = None
    region: Optional[str] = None
    start_date: date
    end_date: Optional[date] = None
    status: ProjectStatus = ProjectStatus.active
    boundary_geom: Optional[Dict] = None  # GeoJSON Polygon

class ProjectResponse(BaseModel):
    project_id: str
    project_code: str
    project_name: str
    description: Optional[str]
    client_name: Optional[str]
    client_email: Optional[str]
    region: Optional[str]
    status: str
    start_date: date
    end_date: Optional[date]
    created_at: datetime

class ProjectMetrics(BaseModel):
    project_id: str
    project_code: str
    project_name: str
    project_status: str
    total_observations: int
    surface_geological_count: int
    structural_count: int
    geothermal_count: int
    soil_profiles: int
    soil_horizons: int
    rock_descriptions: int
    discontinuity_sets: int
    slope_assessments: int
    rock_mass_classifications: int
    geothermal_manifestations: int
    total_samples: int
    samples_completed: int
    total_photos: int
    high_hazard_count: int
    very_high_hazard_count: int
    extreme_hazard_count: int
    max_geothermal_temp: Optional[float]
    avg_geothermal_temp: Optional[float]
    landslide_count: int
    last_observation_at: Optional[datetime]


# ============================================================
# FIELD TRIP MODELS
# ============================================================
class FieldTripCreate(BaseModel):
    project_id: str
    trip_code: str
    trip_date: date
    leader_id: Optional[str] = None
    team_members: Optional[List[str]] = None
    weather_condition: Optional[str] = None
    vehicle_info: Optional[str] = None
    area_visited: Optional[str] = None
    route_geom: Optional[Dict] = None
    notes: Optional[str] = None
    device_id: Optional[str] = None

class FieldTripResponse(BaseModel):
    field_trip_id: str
    project_id: str
    trip_code: str
    trip_date: date
    weather_condition: Optional[str]
    area_visited: Optional[str]
    sync_status: str
    created_at: datetime


# ============================================================
# OBSERVATION MODELS
# ============================================================
class ObservationCreate(BaseModel):
    field_trip_id: str
    site_id: str = Field(..., max_length=50)
    observation_type: ObservationType
    exposure_type: Optional[str] = None
    latitude: float = Field(..., ge=-90, le=90)
    longitude: float = Field(..., ge=-180, le=180)
    elevation_m: Optional[float] = None
    easting: Optional[float] = None
    northing: Optional[float] = None
    utm_zone: str = "37N"
    exposure_length_m: Optional[float] = None
    exposure_height_m: Optional[float] = None
    groundwater_level_m: Optional[float] = None
    weather_condition: Optional[str] = None
    excavation_method: Optional[str] = None
    accessibility: Optional[str] = Field(None, pattern="^(good|fair|poor)$")
    remarks: Optional[str] = None
    logger_id: Optional[str] = None

class ObservationResponse(BaseModel):
    observation_id: str
    field_trip_id: str
    site_id: str
    observation_type: str
    latitude: float
    longitude: float
    elevation_m: Optional[float]
    admin_unit: Optional[str]
    watershed: Optional[str]
    geological_formation: Optional[str]
    status: str
    created_at: datetime

class ObservationMapPoint(BaseModel):
    """Lightweight model for map rendering."""
    observation_id: str
    site_id: str
    observation_type: str
    latitude: float
    longitude: float
    elevation_m: Optional[float]
    has_surface_geological: bool
    has_structural: bool
    has_geothermal: bool
    sample_count: int
    photo_count: int
    status: str
    created_at: datetime


# ============================================================
# SOIL PROFILE & HORIZON MODELS
# ============================================================
class SoilHorizonCreate(BaseModel):
    horizon_label: str = Field(..., max_length=10)
    depth_from_m: float
    depth_to_m: float
    color: Optional[str] = None
    consistency: Optional[str] = None
    moisture: Optional[str] = None
    grain_size: Optional[str] = None
    plasticity: Optional[str] = None
    uscs_class: Optional[str] = None
    organic_content: Optional[str] = None
    boundary_type: Optional[str] = None
    structure_type: Optional[str] = None
    notes: Optional[str] = None
    sort_order: int = 0

class SoilProfileCreate(BaseModel):
    observation_id: str
    profile_code: Optional[str] = None
    total_depth_m: Optional[float] = None
    groundwater_depth_m: Optional[float] = None
    surface_condition: Optional[str] = None
    vegetation_cover: Optional[str] = None
    erosion_evidence: Optional[str] = None
    drainage_class: Optional[str] = None
    dilatancy: Optional[str] = None
    sorting: Optional[str] = None
    notes: Optional[str] = None
    horizons: List[SoilHorizonCreate] = []


# ============================================================
# ROCK DESCRIPTION MODELS
# ============================================================
class RockDescriptionCreate(BaseModel):
    observation_id: str
    rock_name: str = Field(..., max_length=100)
    rock_type: Optional[str] = None
    color: Optional[str] = None
    grain_size: Optional[str] = None
    texture: Optional[str] = None
    weathering_grade: Optional[int] = Field(None, ge=1, le=6)
    intact_strength: Optional[int] = Field(None, ge=1, le=7)
    structure: Optional[str] = None
    mineralogy: Optional[str] = None
    alteration: Optional[str] = None
    rock_description: Optional[str] = None


# ============================================================
# DISCONTINUITY MODELS
# ============================================================
class DiscontinuityCreate(BaseModel):
    observation_id: str
    set_label: str = Field(..., max_length=20)
    disc_type: str
    dip_direction_deg: Optional[float] = Field(None, ge=0, le=360)
    dip_deg: Optional[float] = Field(None, ge=0, le=90)
    spacing_m: Optional[float] = None
    aperture_mm: Optional[float] = None
    persistence_m: Optional[float] = None
    roughness: Optional[str] = None
    infill_material: Optional[str] = None
    infill_thickness_mm: Optional[float] = None
    water_condition: Optional[str] = None
    wall_weathering: Optional[int] = Field(None, ge=1, le=5)
    notes: Optional[str] = None


# ============================================================
# SLOPE STABILITY MODELS
# ============================================================
class SlopeStabilityCreate(BaseModel):
    observation_id: str
    has_existing_slope: bool = True
    slope_height_m: Optional[float] = None
    slope_angle_deg: Optional[float] = None
    aspect_deg: Optional[float] = None
    slope_form: Optional[str] = None
    stability_condition: Optional[str] = None
    hazard_level: HazardLevel = HazardLevel.low
    failure_mode: Optional[str] = None
    factor_of_safety: Optional[float] = None
    trigger_mechanism: Optional[str] = None
    recommended_actions: Optional[str] = None
    mitigation_measures: Optional[str] = None
    monitoring_required: bool = False


# ============================================================
# GEOTHERMAL MODELS
# ============================================================
class GeothermalManifestationCreate(BaseModel):
    observation_id: str
    manifestation_type: ManifestationType
    surface_temp_c: Optional[float] = None
    discharge_rate_lps: Optional[float] = None
    ph_value: Optional[float] = Field(None, ge=-2, le=14)
    electrical_conductivity_us: Optional[float] = None
    total_dissolved_solids: Optional[float] = None
    fluid_color: Optional[str] = None
    odor: Optional[str] = None
    deposit_type: Optional[str] = None
    alteration_zone: Optional[str] = None
    alteration_intensity: Optional[str] = None
    alteration_minerals: Optional[List[str]] = None
    structural_control: Optional[str] = None
    host_rock: Optional[str] = None
    elevation_m: Optional[float] = None
    usage_current: Optional[str] = None
    usage_potential: Optional[str] = None
    notes: Optional[str] = None


# ============================================================
# SAMPLE MODELS
# ============================================================
class SampleCreate(BaseModel):
    observation_id: str
    sample_code: str = Field(..., max_length=50)
    sample_type: SampleType
    depth_m: Optional[float] = None
    horizon_ref: Optional[str] = None
    weight_kg: Optional[float] = None
    sample_condition: Optional[str] = None
    storage_method: Optional[str] = None
    tests_requested: Optional[List[str]] = None
    notes: Optional[str] = None


# ============================================================
# DASHBOARD METRICS MODELS
# ============================================================
class DashboardMetrics(BaseModel):
    total_projects: int
    active_projects: int
    total_observations: int
    observations_today: int
    surface_geological_total: int
    structural_total: int
    geothermal_total: int
    total_samples: int
    samples_pending_lab: int
    high_hazard_count: int
    critical_alerts: int
    landslide_count: int
    max_geothermal_temp: Optional[float]
    avg_geothermal_temp: Optional[float]
    recent_observations: List[ObservationMapPoint]

class ThemeDistribution(BaseModel):
    theme: str
    count: int
    percentage: float
    color: str

class HazardDistribution(BaseModel):
    level: str
    count: int
    color: str

class GeothermalStats(BaseModel):
    manifestation_type: str
    count: int
    avg_temp: Optional[float]
    max_temp: Optional[float]
    avg_ph: Optional[float]


# ============================================================
# EMAIL ALERT MODELS
# ============================================================
class EmailAlert(BaseModel):
    alert_id: int
    project_id: str
    alert_type: str
    severity: str
    message: str
    recipients: List[str]
    is_sent: bool
    created_at: datetime
