//! Project and settings save/load.

use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;

use crate::engine::{LinkState, PatternChange, SequencerSnapshot};
use crate::model;
use crate::settings::Settings;
use crate::state::StagedChange;

use super::App;

impl App {
    /// Persist user preferences (audio, display, link, MIDI) via confy.
    pub fn save_settings(&self, link: &LinkState) {
        let settings = Settings {
            audio: crate::settings::AudioSettings {
                output_device: self.audio.config.output_device.clone(),
                input_device: self.audio.config.input_device.clone(),
                channels: self.audio.config.channels,
                buffer_size: self.audio.config.buffer_size,
                max_voices: self.audio.config.max_voices,
                sample_paths: self.audio.config.sample_paths.clone(),
            },
            display: crate::settings::DisplaySettings {
                fps: self.audio.config.refresh_rate.to_fps(),
                runtime_highlight: self.ui.runtime_highlight,
                show_scope: self.audio.config.show_scope,
                show_spectrum: self.audio.config.show_spectrum,
                show_lissajous: self.audio.config.show_lissajous,
                show_preview: self.audio.config.show_preview,
                show_completion: self.ui.show_completion,
                performance_mode: self.ui.performance_mode,
                color_scheme: self.ui.color_scheme,
                layout: self.audio.config.layout,
                hue_rotation: self.ui.hue_rotation,
                onboarding_dismissed: self.ui.onboarding_dismissed.clone(),
                load_demo_on_startup: self.ui.load_demo_on_startup,
                demo_index: self.ui.demo_index,
                font: self.ui.font.clone(),
                zoom_factor: self.ui.zoom_factor,
                gain_boost: self.audio.config.gain_boost,
                normalize_viz: self.audio.config.normalize_viz,
            },
            link: crate::settings::LinkSettings {
                enabled: link.is_enabled(),
                tempo: link.tempo(),
                quantum: link.quantum(),
            },
            midi: crate::settings::MidiSettings {
                output_devices: {
                    let outputs = crate::midi::list_midi_outputs();
                    self.midi
                        .selected_outputs
                        .iter()
                        .map(|opt| {
                            opt.and_then(|idx| outputs.get(idx).map(|d| d.name.clone()))
                                .unwrap_or_default()
                        })
                        .collect()
                },
                input_devices: {
                    let inputs = crate::midi::list_midi_inputs();
                    self.midi
                        .selected_inputs
                        .iter()
                        .map(|opt| {
                            opt.and_then(|idx| inputs.get(idx).map(|d| d.name.clone()))
                                .unwrap_or_default()
                        })
                        .collect()
                },
            },
        };
        settings.save();
    }

    /// Flush the editor, capture playing state, and write the project file.
    pub fn save(&mut self, path: PathBuf, link: &LinkState, snapshot: &SequencerSnapshot) {
        self.save_editor_to_step();
        if self.page == crate::page::Page::Script {
            self.save_script_from_editor();
        }
        self.project_state.project.sample_paths = self.audio.config.sample_paths.clone();
        self.project_state.project.tempo = link.tempo();
        self.project_state.project.playing_patterns = snapshot
            .active_patterns
            .iter()
            .map(|p| (p.bank, p.pattern))
            .collect();
        match model::save(&self.project_state.project, &path) {
            Ok(final_path) => {
                self.ui
                    .set_status(format!("Saved: {}", final_path.display()));
                self.project_state.file_path = Some(final_path);
            }
            Err(e) => {
                self.ui.set_status(format!("Save error: {e}"));
            }
        }
    }

    pub fn load(&mut self, path: PathBuf, link: &LinkState) {
        match model::load(&path) {
            Ok(project) => {
                self.apply_project(project, format!("Loaded: {}", path.display()), link);
                self.project_state.file_path = Some(path);
            }
            Err(e) => {
                self.ui.set_status(format!("Load error: {e}"));
            }
        }
    }

    /// Replace the current project, reset undo/variables, recompile, and restore playing patterns.
    fn apply_project(&mut self, project: model::Project, label: String, link: &LinkState) {
        let tempo = project.tempo;
        let playing = project.playing_patterns.clone();

        self.project_state.project = project;
        self.editor_ctx.step = 0;
        self.load_step_to_editor();
        self.compile_all_steps(link);
        self.mark_all_patterns_dirty();
        link.set_tempo(tempo);

        self.playback.clear_queues();
        self.undo.clear();
        self.variables.store(Arc::new(HashMap::new()));
        self.dict.lock().clear();

        self.evaluate_prelude(link);
        self.load_script_to_editor();
        self.script_editor.dirty = true;

        for (bank, pattern) in playing {
            self.playback.queued_changes.push(StagedChange {
                change: PatternChange::Start { bank, pattern },
                quantization: crate::model::LaunchQuantization::Immediate,
                sync_mode: crate::model::SyncMode::PhaseLock,
            });
        }

        self.ui.set_status(label);
    }
}
