"""Value source storage using SQLite."""

import uuid
from datetime import datetime, timezone
from typing import List, Optional

from wled_controller.storage.base_sqlite_store import BaseSqliteStore
from wled_controller.storage.database import Database
from wled_controller.storage.utils import resolve_ref
from wled_controller.storage.value_source import (
    VALID_SYSTEM_METRICS,
    AdaptiveValueSource,
    AdaptiveTimeColorValueSource,
    AnimatedColorValueSource,
    AnimatedValueSource,
    AudioValueSource,
    CSSExtractValueSource,
    DaylightValueSource,
    GradientMapValueSource,
    HAEntityValueSource,
    StaticColorValueSource,
    StaticValueSource,
    SystemMetricsValueSource,
    ValueSource,
)
from wled_controller.utils import get_logger

logger = get_logger(__name__)


class ValueSourceStore(BaseSqliteStore[ValueSource]):
    """Persistent storage for value sources."""

    _table_name = "value_sources"
    _entity_name = "Value source"

    def __init__(self, db: Database):
        super().__init__(db, ValueSource.from_dict)

    # Backward-compatible aliases
    get_all_sources = BaseSqliteStore.get_all
    get_source = BaseSqliteStore.get
    delete_source = BaseSqliteStore.delete

    # ── CRUD ─────────────────────────────────────────────────────────

    def create_source(
        self,
        name: str,
        source_type: str,
        value: Optional[float] = None,
        waveform: Optional[str] = None,
        speed: Optional[float] = None,
        min_value: Optional[float] = None,
        max_value: Optional[float] = None,
        audio_source_id: Optional[str] = None,
        mode: Optional[str] = None,
        sensitivity: Optional[float] = None,
        smoothing: Optional[float] = None,
        description: Optional[str] = None,
        schedule: Optional[list] = None,
        picture_source_id: Optional[str] = None,
        scene_behavior: Optional[str] = None,
        auto_gain: Optional[bool] = None,
        use_real_time: Optional[bool] = None,
        latitude: Optional[float] = None,
        color: Optional[list] = None,
        colors: Optional[list] = None,
        easing: Optional[str] = None,
        tags: Optional[List[str]] = None,
        ha_source_id: Optional[str] = None,
        entity_id: Optional[str] = None,
        attribute: Optional[str] = None,
        min_ha_value: Optional[float] = None,
        max_ha_value: Optional[float] = None,
        value_source_id: Optional[str] = None,
        gradient_id: Optional[str] = None,
        color_strip_source_id: Optional[str] = None,
        led_start: Optional[int] = None,
        led_end: Optional[int] = None,
        metric: Optional[str] = None,
        max_rate: Optional[float] = None,
        disk_path: Optional[str] = None,
        sensor_label: Optional[str] = None,
        poll_interval: Optional[float] = None,
    ) -> ValueSource:
        _VALID = (
            "static",
            "animated",
            "audio",
            "adaptive_time",
            "adaptive_scene",
            "daylight",
            "static_color",
            "animated_color",
            "adaptive_time_color",
            "ha_entity",
            "gradient_map",
            "css_extract",
            "system_metrics",
        )
        if source_type not in _VALID:
            raise ValueError(f"Invalid source type: {source_type}")

        self._check_name_unique(name)

        sid = f"vs_{uuid.uuid4().hex[:8]}"
        now = datetime.now(timezone.utc)

        common_tags = tags or []

        if source_type == "static":
            source: ValueSource = StaticValueSource(
                id=sid,
                name=name,
                source_type="static",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                value=value if value is not None else 1.0,
            )
        elif source_type == "animated":
            source = AnimatedValueSource(
                id=sid,
                name=name,
                source_type="animated",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                waveform=waveform or "sine",
                speed=speed if speed is not None else 10.0,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 1.0,
            )
        elif source_type == "audio":
            source = AudioValueSource(
                id=sid,
                name=name,
                source_type="audio",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                audio_source_id=audio_source_id or "",
                mode=mode or "rms",
                sensitivity=sensitivity if sensitivity is not None else 1.0,
                smoothing=smoothing if smoothing is not None else 0.3,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 1.0,
                auto_gain=bool(auto_gain) if auto_gain is not None else False,
            )
        elif source_type == "adaptive_time":
            schedule_data = schedule or []
            if len(schedule_data) < 2:
                raise ValueError("Time of day schedule requires at least 2 points")
            source = AdaptiveValueSource(
                id=sid,
                name=name,
                source_type="adaptive_time",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                schedule=schedule_data,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 1.0,
            )
        elif source_type == "adaptive_scene":
            source = AdaptiveValueSource(
                id=sid,
                name=name,
                source_type="adaptive_scene",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                picture_source_id=picture_source_id or "",
                scene_behavior=scene_behavior or "complement",
                sensitivity=sensitivity if sensitivity is not None else 1.0,
                smoothing=smoothing if smoothing is not None else 0.3,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 1.0,
            )
        elif source_type == "daylight":
            source = DaylightValueSource(
                id=sid,
                name=name,
                source_type="daylight",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                speed=speed if speed is not None else 1.0,
                use_real_time=bool(use_real_time) if use_real_time is not None else False,
                latitude=latitude if latitude is not None else 50.0,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 1.0,
            )
        elif source_type == "static_color":
            source = StaticColorValueSource(
                id=sid,
                name=name,
                source_type="static_color",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                color=color if isinstance(color, list) and len(color) == 3 else [255, 255, 255],
            )
        elif source_type == "animated_color":
            source = AnimatedColorValueSource(
                id=sid,
                name=name,
                source_type="animated_color",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                colors=(
                    colors
                    if isinstance(colors, list) and len(colors) >= 2
                    else [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
                ),
                speed=speed if speed is not None else 10.0,
                easing=easing or "linear",
            )
        elif source_type == "adaptive_time_color":
            schedule_data = schedule or []
            if len(schedule_data) < 2:
                raise ValueError("Color schedule requires at least 2 points")
            source = AdaptiveTimeColorValueSource(
                id=sid,
                name=name,
                source_type="adaptive_time_color",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                schedule=schedule_data,
            )
        elif source_type == "ha_entity":
            if not ha_source_id:
                raise ValueError("HA source ID is required for ha_entity type")
            if not entity_id:
                raise ValueError("Entity ID is required for ha_entity type")
            source = HAEntityValueSource(
                id=sid,
                name=name,
                source_type="ha_entity",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                ha_source_id=ha_source_id,
                entity_id=entity_id,
                attribute=attribute or "",
                min_ha_value=min_ha_value if min_ha_value is not None else 0.0,
                max_ha_value=max_ha_value if max_ha_value is not None else 100.0,
                smoothing=smoothing if smoothing is not None else 0.0,
            )
        elif source_type == "gradient_map":
            if not value_source_id:
                raise ValueError("Value source ID is required for gradient_map type")
            source = GradientMapValueSource(
                id=sid,
                name=name,
                source_type="gradient_map",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                value_source_id=value_source_id,
                gradient_id=gradient_id or "",
                easing=easing or "linear",
            )
        elif source_type == "css_extract":
            if not color_strip_source_id:
                raise ValueError("Color strip source ID is required for css_extract type")
            source = CSSExtractValueSource(
                id=sid,
                name=name,
                source_type="css_extract",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                color_strip_source_id=color_strip_source_id,
                led_start=led_start if led_start is not None else 0,
                led_end=led_end if led_end is not None else -1,
            )
        elif source_type == "system_metrics":
            m = metric or "cpu_load"
            if m not in VALID_SYSTEM_METRICS:
                raise ValueError(f"Invalid metric: {m}")
            source = SystemMetricsValueSource(
                id=sid,
                name=name,
                source_type="system_metrics",
                created_at=now,
                updated_at=now,
                description=description,
                tags=common_tags,
                metric=m,
                min_value=min_value if min_value is not None else 0.0,
                max_value=max_value if max_value is not None else 100.0,
                max_rate=max_rate if max_rate is not None else 125_000_000.0,
                disk_path=disk_path or "",
                sensor_label=sensor_label or "",
                poll_interval=poll_interval if poll_interval is not None else 1.0,
                smoothing=smoothing if smoothing is not None else 0.0,
            )

        self._items[sid] = source
        self._save_item(sid, source)

        logger.info(f"Created value source: {name} ({sid}, type={source_type})")
        return source

    def update_source(
        self,
        source_id: str,
        name: Optional[str] = None,
        value: Optional[float] = None,
        waveform: Optional[str] = None,
        speed: Optional[float] = None,
        min_value: Optional[float] = None,
        max_value: Optional[float] = None,
        audio_source_id: Optional[str] = None,
        mode: Optional[str] = None,
        sensitivity: Optional[float] = None,
        smoothing: Optional[float] = None,
        description: Optional[str] = None,
        schedule: Optional[list] = None,
        picture_source_id: Optional[str] = None,
        scene_behavior: Optional[str] = None,
        auto_gain: Optional[bool] = None,
        use_real_time: Optional[bool] = None,
        latitude: Optional[float] = None,
        color: Optional[list] = None,
        colors: Optional[list] = None,
        easing: Optional[str] = None,
        tags: Optional[List[str]] = None,
        ha_source_id: Optional[str] = None,
        entity_id: Optional[str] = None,
        attribute: Optional[str] = None,
        min_ha_value: Optional[float] = None,
        max_ha_value: Optional[float] = None,
        value_source_id: Optional[str] = None,
        gradient_id: Optional[str] = None,
        color_strip_source_id: Optional[str] = None,
        led_start: Optional[int] = None,
        led_end: Optional[int] = None,
        metric: Optional[str] = None,
        max_rate: Optional[float] = None,
        disk_path: Optional[str] = None,
        sensor_label: Optional[str] = None,
        poll_interval: Optional[float] = None,
    ) -> ValueSource:
        source = self.get(source_id)

        if name is not None:
            self._check_name_unique(name, exclude_id=source_id)
            source.name = name

        if description is not None:
            source.description = description
        if tags is not None:
            source.tags = tags

        if isinstance(source, StaticValueSource):
            if value is not None:
                source.value = value
        elif isinstance(source, AnimatedValueSource):
            if waveform is not None:
                source.waveform = waveform
            if speed is not None:
                source.speed = speed
            if min_value is not None:
                source.min_value = min_value
            if max_value is not None:
                source.max_value = max_value
        elif isinstance(source, AudioValueSource):
            if audio_source_id is not None:
                source.audio_source_id = resolve_ref(audio_source_id, source.audio_source_id)
            if mode is not None:
                source.mode = mode
            if sensitivity is not None:
                source.sensitivity = sensitivity
            if smoothing is not None:
                source.smoothing = smoothing
            if min_value is not None:
                source.min_value = min_value
            if max_value is not None:
                source.max_value = max_value
            if auto_gain is not None:
                source.auto_gain = auto_gain
        elif isinstance(source, AdaptiveValueSource):
            if schedule is not None:
                if source.source_type == "adaptive_time" and len(schedule) < 2:
                    raise ValueError("Time of day schedule requires at least 2 points")
                source.schedule = schedule
            if picture_source_id is not None:
                source.picture_source_id = resolve_ref(picture_source_id, source.picture_source_id)
            if scene_behavior is not None:
                source.scene_behavior = scene_behavior
            if sensitivity is not None:
                source.sensitivity = sensitivity
            if smoothing is not None:
                source.smoothing = smoothing
            if min_value is not None:
                source.min_value = min_value
            if max_value is not None:
                source.max_value = max_value
        elif isinstance(source, DaylightValueSource):
            if speed is not None:
                source.speed = speed
            if use_real_time is not None:
                source.use_real_time = use_real_time
            if latitude is not None:
                source.latitude = latitude
            if min_value is not None:
                source.min_value = min_value
            if max_value is not None:
                source.max_value = max_value
        elif isinstance(source, StaticColorValueSource):
            if color is not None and isinstance(color, list) and len(color) == 3:
                source.color = color
        elif isinstance(source, AnimatedColorValueSource):
            if colors is not None and isinstance(colors, list):
                source.colors = colors
            if speed is not None:
                source.speed = speed
            if easing is not None:
                source.easing = easing
        elif isinstance(source, AdaptiveTimeColorValueSource):
            if schedule is not None:
                if len(schedule) < 2:
                    raise ValueError("Color schedule requires at least 2 points")
                source.schedule = schedule
        elif isinstance(source, HAEntityValueSource):
            if ha_source_id is not None:
                source.ha_source_id = resolve_ref(ha_source_id, source.ha_source_id)
            if entity_id is not None:
                source.entity_id = entity_id
            if attribute is not None:
                source.attribute = attribute
            if min_ha_value is not None:
                source.min_ha_value = min_ha_value
            if max_ha_value is not None:
                source.max_ha_value = max_ha_value
            if smoothing is not None:
                source.smoothing = smoothing
        elif isinstance(source, GradientMapValueSource):
            if value_source_id is not None:
                source.value_source_id = resolve_ref(value_source_id, source.value_source_id)
            if gradient_id is not None:
                source.gradient_id = resolve_ref(gradient_id, source.gradient_id)
            if easing is not None:
                source.easing = easing
        elif isinstance(source, CSSExtractValueSource):
            if color_strip_source_id is not None:
                source.color_strip_source_id = resolve_ref(
                    color_strip_source_id, source.color_strip_source_id
                )
            if led_start is not None:
                source.led_start = led_start
            if led_end is not None:
                source.led_end = led_end
        elif isinstance(source, SystemMetricsValueSource):
            if metric is not None:
                if metric not in VALID_SYSTEM_METRICS:
                    raise ValueError(f"Invalid metric: {metric}")
                source.metric = metric
            if min_value is not None:
                source.min_value = min_value
            if max_value is not None:
                source.max_value = max_value
            if max_rate is not None:
                source.max_rate = max_rate
            if disk_path is not None:
                source.disk_path = disk_path
            if sensor_label is not None:
                source.sensor_label = sensor_label
            if poll_interval is not None:
                source.poll_interval = poll_interval
            if smoothing is not None:
                source.smoothing = smoothing

        source.updated_at = datetime.now(timezone.utc)
        self._save_item(source_id, source)

        logger.info(f"Updated value source: {source_id}")
        return source
