"""Reusable scene activation and snapshot capture logic.

These functions are used by both the scene-presets API route and the automation engine.
"""

from typing import List, Optional, Set, Tuple

from wled_controller.core.processing.processor_manager import ProcessorManager
from wled_controller.storage.output_target_store import OutputTargetStore
from wled_controller.storage.scene_preset import (
    ScenePreset,
    TargetSnapshot,
)
from wled_controller.utils import get_logger

logger = get_logger(__name__)


def capture_current_snapshot(
    target_store: OutputTargetStore,
    processor_manager: ProcessorManager,
    target_ids: Optional[Set[str]] = None,
) -> List[TargetSnapshot]:
    """Capture current target state as a snapshot list.

    Args:
        target_ids: If provided, only capture these targets. Captures all if None.
    """
    targets = []
    for t in target_store.get_all_targets():
        if target_ids is not None and t.id not in target_ids:
            continue
        proc = processor_manager.get_processor(t.id)
        running = proc.is_running if proc else False
        targets.append(TargetSnapshot(
            target_id=t.id,
            running=running,
            color_strip_source_id=getattr(t, "color_strip_source_id", ""),
            brightness_value_source_id=getattr(t, "brightness_value_source_id", ""),
            fps=getattr(t, "fps", 30),
        ))

    return targets


async def apply_scene_state(
    preset: ScenePreset,
    target_store: OutputTargetStore,
    processor_manager: ProcessorManager,
) -> Tuple[str, List[str]]:
    """Apply a scene preset's state to the system.

    Args:
        preset: The scene preset to activate.
        target_store: Target store for reading/updating targets.
        processor_manager: Processor manager for starting/stopping targets.

    Returns:
        (status, errors) where status is "activated" or "partial" and
        errors is a list of error messages.
    """
    errors: List[str] = []

    # 1. Stop targets that should be stopped
    for ts in preset.targets:
        if not ts.running:
            try:
                proc = processor_manager.get_processor(ts.target_id)
                if proc and proc.is_running:
                    await processor_manager.stop_processing(ts.target_id)
            except Exception as e:
                errors.append(f"Stop target {ts.target_id}: {e}")

    # 2. Update target configs (CSS, brightness source, FPS)
    for ts in preset.targets:
        try:
            target = target_store.get_target(ts.target_id)
            changed = {}
            if getattr(target, "color_strip_source_id", None) != ts.color_strip_source_id:
                changed["color_strip_source_id"] = ts.color_strip_source_id
            if getattr(target, "brightness_value_source_id", None) != ts.brightness_value_source_id:
                changed["brightness_value_source_id"] = ts.brightness_value_source_id
            if getattr(target, "fps", None) != ts.fps:
                changed["fps"] = ts.fps
            if changed:
                target.update_fields(**changed)
                target_store.update_target(ts.target_id, **changed)

                # Sync live processor if running
                proc = processor_manager.get_processor(ts.target_id)
                if proc and proc.is_running:
                    css_changed = "color_strip_source_id" in changed
                    bvs_changed = "brightness_value_source_id" in changed
                    settings_changed = "fps" in changed
                    if css_changed:
                        target.sync_with_manager(processor_manager, settings_changed=False, css_changed=True)
                    if bvs_changed:
                        target.sync_with_manager(processor_manager, settings_changed=False, brightness_vs_changed=True)
                    if settings_changed:
                        target.sync_with_manager(processor_manager, settings_changed=True)
        except ValueError:
            errors.append(f"Target {ts.target_id} not found (skipped)")
        except Exception as e:
            errors.append(f"Target {ts.target_id} config: {e}")

    # 3. Start targets that should be running
    for ts in preset.targets:
        if ts.running:
            try:
                proc = processor_manager.get_processor(ts.target_id)
                if not proc or not proc.is_running:
                    await processor_manager.start_processing(ts.target_id)
            except Exception as e:
                errors.append(f"Start target {ts.target_id}: {e}")

    status = "activated" if not errors else "partial"
    if errors:
        logger.warning(f"Scene activation errors: {errors}")

    return status, errors
