"""Live processing settings model and atomic JSON writer."""

from __future__ import annotations

from dataclasses import dataclass
import json
from pathlib import Path


@dataclass(slots=True)
class ProcessingLiveConfig:
    """Runtime-adjustable processing parameters shared with data processor."""

    processor_mode: str = "pass_through"
    pass_through_channel: str = "s21"
    pass_through_fixed_y_enabled: bool = False
    pass_through_y_min_db: float = -100.0
    pass_through_y_max_db: float = 0.0
    bscan_axis: str = "abs"
    bscan_channel: str = "s21"
    bscan_cut_m: float = 0.824
    bscan_max_depth_m: float = 1.0
    bscan_gain: float = 1.0
    bscan_start_freq_mhz: float = 100.0
    bscan_stop_freq_mhz: float = 8800.0
    legacy_gpr_mode: str = "point"
    gpr_input_positions: list[int] | None = None
    gpr_output_positions: list[int] | None = None
    gpr_min_depth_m: float = 2.0
    gpr_max_depth_m: float = 14.0
    gpr_range_comp_power: float = 0.28
    gpr_angle_comp_power: float = 0.10
    gpr_comp_power: float = 0.2
    gpr_score_mode: str = "combined"
    gpr_max_detected_objects_to_draw: int = 5
    gpr_draw_top_m_objects: int = 2
    gpr_speed_m_s: float = 0.0
    gpr_look_angle_deg: float = 0.0
    gpr_snr_thresh: float = 4.5
    gpr_snr_comp_max: float = 25.0
    gpr_start_freq_mhz: float = 3000.0
    gpr_stop_freq_mhz: float = 6000.0
    gpr_background_subtract_enabled: bool = True
    gpr_background_mean_count: int = 10
    gpr_remove_sidelobe_objects_enabled: bool = True
    gpr_imaging_plane_y_m: float = 0.0
    reprocess_current_result: bool = True
    history_command_seq: int = 0
    history_command: str = "none"

    def __post_init__(self) -> None:
        """Normalize optional list fields to concrete integer lists."""
        if self.gpr_input_positions is None:
            self.gpr_input_positions = []
        else:
            self.gpr_input_positions = [int(value) for value in self.gpr_input_positions]
        if self.gpr_output_positions is None:
            self.gpr_output_positions = []
        else:
            self.gpr_output_positions = [int(value) for value in self.gpr_output_positions]

    def to_dict(self) -> dict[str, object]:
        """Convert live config to JSON-serializable dictionary."""
        return {
            "processor_mode": str(self.processor_mode),
            "pass_through_channel": str(self.pass_through_channel),
            "pass_through_fixed_y_enabled": bool(self.pass_through_fixed_y_enabled),
            "pass_through_y_min_db": float(self.pass_through_y_min_db),
            "pass_through_y_max_db": float(self.pass_through_y_max_db),
            "bscan_axis": str(self.bscan_axis),
            "bscan_channel": str(self.bscan_channel),
            "bscan_cut_m": float(self.bscan_cut_m),
            "bscan_max_depth_m": float(self.bscan_max_depth_m),
            "bscan_gain": float(self.bscan_gain),
            "bscan_start_freq_mhz": float(self.bscan_start_freq_mhz),
            "bscan_stop_freq_mhz": float(self.bscan_stop_freq_mhz),
            "legacy_gpr_mode": str(self.legacy_gpr_mode),
            "gpr_input_positions": [int(value) for value in self.gpr_input_positions],
            "gpr_output_positions": [int(value) for value in self.gpr_output_positions],
            "gpr_min_depth_m": float(self.gpr_min_depth_m),
            "gpr_max_depth_m": float(self.gpr_max_depth_m),
            "gpr_range_comp_power": float(self.gpr_range_comp_power),
            "gpr_angle_comp_power": float(self.gpr_angle_comp_power),
            "gpr_comp_power": float(self.gpr_comp_power),
            "gpr_score_mode": str(self.gpr_score_mode),
            "gpr_max_detected_objects_to_draw": int(self.gpr_max_detected_objects_to_draw),
            "gpr_draw_top_m_objects": int(self.gpr_draw_top_m_objects),
            "gpr_speed_m_s": float(self.gpr_speed_m_s),
            "gpr_look_angle_deg": float(self.gpr_look_angle_deg),
            "gpr_snr_thresh": float(self.gpr_snr_thresh),
            "gpr_snr_comp_max": float(self.gpr_snr_comp_max),
            "gpr_start_freq_mhz": float(self.gpr_start_freq_mhz),
            "gpr_stop_freq_mhz": float(self.gpr_stop_freq_mhz),
            "gpr_background_subtract_enabled": bool(self.gpr_background_subtract_enabled),
            "gpr_background_mean_count": int(self.gpr_background_mean_count),
            "gpr_remove_sidelobe_objects_enabled": bool(self.gpr_remove_sidelobe_objects_enabled),
            "gpr_imaging_plane_y_m": float(self.gpr_imaging_plane_y_m),
            "reprocess_current_result": bool(self.reprocess_current_result),
            "history_command_seq": int(self.history_command_seq),
            "history_command": str(self.history_command),
        }


class ProcessingLiveConfigWriter:
    """Atomic writer for processing live-config file."""

    def __init__(self, config_path: Path) -> None:
        """Create writer targeting `config_path`."""
        self._config_path = config_path
        self._config_path.parent.mkdir(parents=True, exist_ok=True)

    @property
    def path(self) -> Path:
        """Return destination config path."""
        return self._config_path

    def write(self, config: ProcessingLiveConfig) -> Path:
        """Atomically write config by temp-file replace."""
        temp_path = self._config_path.with_suffix(self._config_path.suffix + ".tmp")
        temp_path.write_text(json.dumps(config.to_dict(), indent=2), encoding="utf-8")
        temp_path.replace(self._config_path)
        return self._config_path
