"""Dataclass schema for full GUI config profiles."""

from __future__ import annotations

from copy import deepcopy
from dataclasses import dataclass, field
import json
from pathlib import Path
from typing import Any

from python_app.models.run_config_model import RunConfigModel


@dataclass(slots=True)
class GuiSwitchStateModel:
    """UI-only state for switch selection controls."""

    combo_mode: str = "text"
    combos_text: str = ""
    single_input: str = "0"
    single_output: str = "0"


@dataclass(slots=True)
class GuiPassThroughStateModel:
    """UI-only defaults for pass-through rendering."""

    show_magnitude: bool = True
    show_phase: bool = True
    combo_filter: str = ""
    fixed_y_enabled: bool = False
    y_min_db: float = -100.0
    y_max_db: float = 0.0


@dataclass(slots=True)
class GuiBscanStateModel:
    """UI-only defaults for B-scan live settings."""

    axis: str = "abs"
    cut_m: float = 0.824
    max_depth_m: float = 1.0
    gain: float = 1.0
    start_freq_mhz: float = 100.0
    stop_freq_mhz: float = 8800.0
    subtract_mean_ascan: bool = False


@dataclass(slots=True)
class GuiGprStateModel:
    """UI-only defaults for coherent backprojection GPR live settings."""

    input_positions: str = ""
    output_positions: str = ""
    min_depth_m: float = 2.0
    max_depth_m: float = 14.0
    range_comp_power: float = 0.28
    angle_comp_power: float = 0.10
    score_mode: str = "combined"
    max_detected_objects_to_draw: int = 5
    draw_top_m_objects: int = 2
    start_freq_mhz: float = 3000.0
    stop_freq_mhz: float = 6000.0
    background_subtract_enabled: bool = True
    background_mean_count: int = 10
    remove_sidelobe_objects_enabled: bool = True
    imaging_plane_y_m: float = 0.0
    render_mode: str = "heatmap"
    min_visible_score: float = 0.0
    visible_x_min_m: float = -2.0
    visible_x_max_m: float = 2.0
    visible_z_min_m: float = 0.0
    visible_z_max_m: float = 14.0


@dataclass(slots=True)
class GuiLegacyGprStateModel:
    """UI-only defaults for legacy point/extended GPR live settings."""

    mode: str = "point"
    input_positions: str = ""
    output_positions: str = ""
    min_depth_m: float = 2.0
    max_depth_m: float = 14.0
    comp_power: float = 0.2
    start_freq_mhz: float = 3000.0
    stop_freq_mhz: float = 6000.0
    speed_m_s: float = 0.0
    ignore_socket_speed_enabled: bool = False
    look_angle_deg: float = 0.0
    snr_thresh: float = 4.5
    snr_comp_max: float = 25.0
    background_subtract_enabled: bool = True
    background_mean_count: int = 10
    render_mode: str = "heatmap"
    min_visible_pair_count: int = 1
    visible_x_min_m: float = -2.0
    visible_x_max_m: float = 2.0
    visible_z_min_m: float = 0.0
    visible_z_max_m: float = 14.0


@dataclass(slots=True)
class GuiProcessingStateModel:
    """UI-only processing-section defaults."""

    selected_mode: str = "pass_through"
    pass_through: GuiPassThroughStateModel = field(default_factory=GuiPassThroughStateModel)
    bscan: GuiBscanStateModel = field(default_factory=GuiBscanStateModel)
    gpr: GuiGprStateModel = field(default_factory=GuiGprStateModel)
    legacy_gpr: GuiLegacyGprStateModel = field(default_factory=GuiLegacyGprStateModel)


@dataclass(slots=True)
class GuiDataActionsStateModel:
    """UI-only defaults for snapshot/export controls."""

    save_count: int = 10
    save_path: str = ""
    save_name: str = "snapshot_manual"


@dataclass(slots=True)
class GuiPreprocessDialogStateModel:
    """UI-only defaults for preprocessing dialog controls."""

    set_name: str = "set_001"
    radar_config_dir: str = ""
    use_all_radar_configs: bool = False


@dataclass(slots=True)
class GuiStateModel:
    """GUI-only profile payload stored under top-level `gui`."""

    version: int = 1
    switches: GuiSwitchStateModel = field(default_factory=GuiSwitchStateModel)
    processing: GuiProcessingStateModel = field(default_factory=GuiProcessingStateModel)
    data_actions: GuiDataActionsStateModel = field(default_factory=GuiDataActionsStateModel)
    preprocess_dialog: GuiPreprocessDialogStateModel = field(default_factory=GuiPreprocessDialogStateModel)


@dataclass(slots=True)
class GuiProfileModel:
    """Full GUI profile with pipeline-compatible root config and optional UI state."""

    run_config: RunConfigModel = field(default_factory=RunConfigModel)
    gui: GuiStateModel | None = None

    @staticmethod
    def from_dict(payload: dict[str, Any]) -> GuiProfileModel:
        """Build profile from JSON-like payload using codec layer."""
        from python_app.models.gui_profile_codec import gui_profile_from_dict

        return gui_profile_from_dict(payload)

    @classmethod
    def load_from_path(cls, path: Path) -> GuiProfileModel:
        """Load JSON file from disk and decode into profile model."""
        payload = json.loads(path.read_text(encoding="utf-8"))
        if not isinstance(payload, dict):
            raise ValueError(f"Config profile root must be JSON object: {path}")
        return cls.from_dict(payload)

    def clone(self) -> GuiProfileModel:
        """Create deep copy while preserving whether `gui` is absent."""
        return deepcopy(self)

    def to_dict(self) -> dict[str, Any]:
        """Encode profile into JSON-serializable dictionary."""
        from python_app.models.gui_profile_codec import gui_profile_to_dict

        return gui_profile_to_dict(self)
