use super::*;
use iced::widget::column;

impl App {
    pub(super) fn preferences_panel(&self) -> Element<'_, Message> {
        let dspark_toggle: Option<fn(bool) -> Message> = self
            .preference_draft
            .model
            .supports_dspark()
            .then_some(Message::PreferenceDsparkChanged);
        let dspark = checkbox(
            "Enable DSpark for this model",
            self.preference_draft.dspark_enabled,
        )
        .on_toggle_maybe(dspark_toggle);
        let glm_mtp_toggle: Option<fn(bool) -> Message> = (self.preference_draft.model
            == ModelChoice::Glm52)
            .then_some(Message::PreferenceGlmMtpChanged);
        let glm_mtp_timing_toggle: Option<fn(bool) -> Message> = (self.preference_draft.model
            == ModelChoice::Glm52)
            .then_some(Message::PreferenceGlmMtpTimingChanged);
        let dspark_strict_toggle: Option<fn(bool) -> Message> = self
            .preference_draft
            .model
            .supports_dspark()
            .then_some(Message::PreferenceDsparkStrictChanged);
        let effective = self
            .preference_draft
            .generation()
            .and_then(|generation| {
                self.preference_draft.runtime().and_then(|runtime| {
                    crate::settings::effective_settings(
                        self.preference_draft.model,
                        &generation,
                        &runtime,
                        &models_path(),
                    )
                })
            })
            .ok();
        let engine = effective.as_ref().map(|settings| &settings.engine);
        let turn = effective.as_ref().map(|settings| &settings.turn);
        let mut power = text_input("100", &self.preference_draft.power_percent);
        let mut prefill = text_input("Automatic", &self.preference_draft.prefill_chunk);
        let mut ssd_full_layers = text_input("Automatic", &self.preference_draft.ssd_full_layers);
        let mut steering_file = text_input(
            "Direction-vector file path",
            &self.preference_draft.directional_steering_file,
        );
        let mut steering_ffn =
            text_input("Automatic", &self.preference_draft.directional_steering_ffn);
        let mut steering_attn = text_input("0", &self.preference_draft.directional_steering_attn);
        let mut dspark_confidence = text_input(
            "0.9 (DS4 default)",
            &self.preference_draft.dspark_confidence_threshold,
        );
        if self.preference_draft.model != ModelChoice::Glm52 {
            power = power.on_input(Message::PreferencePowerChanged);
            prefill = prefill.on_input(Message::PreferencePrefillChunkChanged);
            steering_file = steering_file.on_input(Message::PreferenceSteeringFileChanged);
            steering_ffn = steering_ffn.on_input(Message::PreferenceSteeringFfnChanged);
            steering_attn = steering_attn.on_input(Message::PreferenceSteeringAttnChanged);
        } else {
            ssd_full_layers = ssd_full_layers.on_input(Message::PreferenceSsdFullLayersChanged);
        }
        if self.preference_draft.model.supports_dspark() {
            dspark_confidence =
                dspark_confidence.on_input(Message::PreferenceDsparkConfidenceChanged);
        }

        let model_group = preference_group(
            "MODEL & LIFECYCLE",
            column![
                pick_list(
                    &MODEL_CHOICES[..],
                    Some(self.preference_draft.model),
                    Message::PreferenceModelChanged,
                )
                .width(Length::Fill),
                text(format!(
                    "Main: {}{}",
                    engine.map_or_else(
                        || "Invalid settings".to_owned(),
                        |engine| engine.artifacts.model.display().to_string(),
                    ),
                    engine
                        .and_then(|engine| engine.artifacts.mtp.as_ref())
                        .map_or_else(String::new, |path| format!(
                            " • support: {}",
                            path.display()
                        )),
                ))
                .size(12),
                row![
                    text_input("10", &self.preference_draft.idle_timeout_minutes)
                        .on_input(Message::PreferenceTimeoutChanged)
                        .width(90)
                        .padding(9),
                    text("minutes before unloading the model").size(13),
                ]
                .spacing(10)
                .align_y(Alignment::Center),
                text("Enter a whole number from 1 to 1440.").size(12),
            ]
            .spacing(10),
        );
        let endpoint_group = preference_group(
            "LOCAL ENDPOINT",
            column![
                preference_input_row(
                    "Port",
                    text_input("4000", &self.preference_draft.endpoint_port)
                        .on_input(Message::PreferenceEndpointPortChanged),
                ),
                text("Listens on 127.0.0.1. Saving a changed port restarts the local endpoint.")
                    .size(12),
            ]
            .spacing(10),
        );
        let generation_group = preference_group(
            "GENERATION",
            column![
                preference_input_row(
                    "Context tokens",
                    text_input("32768", &self.preference_draft.context_tokens)
                        .on_input(Message::PreferenceContextChanged),
                ),
                preference_input_row(
                    "Maximum generated tokens",
                    text_input("50000", &self.preference_draft.max_generated_tokens)
                        .on_input(Message::PreferenceMaxTokensChanged),
                ),
                text("System prompt").size(13),
                text_input(
                    "You are a helpful assistant",
                    &self.preference_draft.system_prompt,
                )
                .on_input(Message::PreferenceSystemPromptChanged)
                .padding(9),
                Space::with_height(4),
                text("SAMPLING & REASONING").size(11).color(muted_text()),
                preference_input_row(
                    "Temperature",
                    text_input("DS4 default", &self.preference_draft.temperature)
                        .on_input(Message::PreferenceTemperatureChanged),
                ),
                preference_input_row(
                    "Top-p",
                    text_input("DS4 default", &self.preference_draft.top_p)
                        .on_input(Message::PreferenceTopPChanged),
                ),
                preference_input_row(
                    "Min-p",
                    text_input("DS4 default", &self.preference_draft.min_p)
                        .on_input(Message::PreferenceMinPChanged),
                ),
                preference_input_row(
                    "Seed",
                    text_input("Random", &self.preference_draft.seed)
                        .on_input(Message::PreferenceSeedChanged),
                ),
                row![
                    text("Reasoning").size(13).width(Length::Fill),
                    pick_list(
                        &REASONING_MODES[..],
                        Some(self.preference_draft.reasoning_mode),
                        Message::PreferenceReasoningChanged,
                    )
                    .width(240),
                ]
                .spacing(12)
                .align_y(Alignment::Center),
                text("Blank sampling values retain DS4's model-family defaults. Think Max needs at least 393216 context tokens.")
                    .size(12),
                text(turn.map_or_else(
                    || "Effective settings will appear after valid values are entered.".to_owned(),
                    |settings| format!(
                        "Effective: {} context • {} max • temp {} • top-p {} • min-p {} • seed {} • {} • system prompt {}",
                        settings.context_tokens,
                        settings.max_generated_tokens,
                        settings.temperature,
                        settings.top_p,
                        settings.min_p,
                        settings.seed.map_or_else(|| "random".to_owned(), |seed| seed.to_string()),
                        settings.reasoning_mode,
                        if settings.system_prompt.is_empty() { "off" } else { "on" },
                    ),
                ))
                .size(12),
            ]
            .spacing(10),
        );
        let execution_group = preference_group(
            "EXECUTION",
            column![
                preference_input_row(
                    "CPU helper threads",
                    text_input("Automatic", &self.preference_draft.cpu_threads)
                        .on_input(Message::PreferenceCpuThreadsChanged),
                ),
                preference_input_row("GPU power percent", power),
                preference_input_row("Prefill chunk", prefill),
                checkbox("Prefer exact quality kernels", self.preference_draft.quality)
                    .on_toggle(Message::PreferenceQualityChanged),
                checkbox("Warm mapped weights at load time", self.preference_draft.warm_weights)
                    .on_toggle(Message::PreferenceWarmWeightsChanged),
                text(if self.preference_draft.model == ModelChoice::Glm52 {
                    "GLM 5.2 uses full GPU power and selects prefill chunks automatically."
                } else {
                    "Blank numeric values preserve DS4's automatic engine behavior."
                })
                .size(12),
                text(engine.as_ref().map_or_else(
                    || "Effective execution settings will appear after valid values are entered."
                        .to_owned(),
                    |engine| {
                        let settings = engine.execution;
                        format!(
                            "Metal engine: threads {} • power {}% • prefill {} • quality {} • warm weights {}",
                            if settings.cpu_threads == 0 { "auto".to_owned() } else { settings.cpu_threads.to_string() },
                            if settings.power_percent == 0 { 100 } else { settings.power_percent },
                            if settings.prefill_chunk == 0 { "auto".to_owned() } else { settings.prefill_chunk.to_string() },
                            if settings.quality { "on" } else { "off" },
                            if settings.warm_weights { "on" } else { "off" },
                        )
                    },
                ))
                .size(12),
            ]
            .spacing(10),
        );
        let acceleration_group = preference_group(
            "ACCELERATION & MEMORY",
            column![
                text("SPECULATIVE DECODING").size(11).color(muted_text()),
                preference_input_row(
                    "MTP draft tokens",
                    text_input("1", &self.preference_draft.mtp_draft_tokens)
                        .on_input(Message::PreferenceMtpDraftChanged),
                ),
                preference_input_row(
                    "MTP verifier margin",
                    text_input("3", &self.preference_draft.mtp_margin)
                        .on_input(Message::PreferenceMtpMarginChanged),
                ),
                checkbox("Enable integrated GLM MTP", self.preference_draft.glm_mtp)
                    .on_toggle_maybe(glm_mtp_toggle),
                checkbox(
                    "Log GLM MTP timing counters",
                    self.preference_draft.glm_mtp_timing,
                )
                .on_toggle_maybe(glm_mtp_timing_toggle),
                dspark,
                preference_input_row("DSpark confidence threshold", dspark_confidence),
                checkbox(
                    "DSpark target-only decode",
                    self.preference_draft.dspark_strict,
                )
                .on_toggle_maybe(dspark_strict_toggle),
                text(if self.preference_draft.model.supports_dspark() {
                    "DSpark uses the managed support artifact; entering a threshold or enabling strict mode also enables DSpark."
                } else if self.preference_draft.model == ModelChoice::Glm52 {
                    "GLM MTP is integrated; DSpark is unavailable for this model."
                } else {
                    "No managed MTP support artifact is available for this model."
                })
                .size(12),
                text(engine.as_ref().map_or_else(
                    || "Effective speculative settings will appear after valid values are entered."
                        .to_owned(),
                    |engine| {
                        let settings = engine.speculative;
                        format!(
                            "Engine: MTP draft {} • margin {} • GLM MTP {} • timing {} • DSpark {} • confidence {}{} • target-only {}",
                            settings.mtp_draft_tokens,
                            settings.mtp_margin,
                            if settings.glm_mtp { "on" } else { "off" },
                            if settings.glm_mtp_timing { "on" } else { "off" },
                            if settings.dspark { "on" } else { "off" },
                            settings.dspark_confidence_threshold,
                            if settings.dspark_confidence_threshold_set { " explicit" } else { " default" },
                            if settings.dspark_strict { "on" } else { "off" },
                        )
                    },
                ))
                .size(12),
                Space::with_height(6),
                text("SSD STREAMING").size(11).color(muted_text()),
                checkbox("Enable SSD-backed model streaming", self.preference_draft.ssd_streaming)
                    .on_toggle(Message::PreferenceSsdChanged),
                checkbox("Skip automatic expert preload", self.preference_draft.ssd_streaming_cold)
                    .on_toggle(Message::PreferenceSsdColdChanged),
                preference_input_row(
                    "Expert cache count or GiB",
                    text_input("Automatic, 128, or 64GB", &self.preference_draft.ssd_cache)
                        .on_input(Message::PreferenceSsdCacheChanged),
                ),
                preference_input_row("Fully resident GLM layers", ssd_full_layers),
                preference_input_row(
                    "Explicit expert preload count",
                    text_input("Automatic", &self.preference_draft.ssd_preload_experts)
                        .on_input(Message::PreferenceSsdPreloadChanged),
                ),
                text("A blank full-layer value is automatic; an explicit 0 disables fully resident GLM layers. SSD streaming and DSpark are mutually exclusive.")
                    .size(12),
                text(engine.as_ref().map_or_else(
                    || "Effective SSD settings will appear after valid values are entered."
                        .to_owned(),
                    |engine| {
                        let settings = engine.ssd;
                        let cache = if settings.cache_bytes > 0 {
                            format!("{} GiB", settings.cache_bytes / GIB)
                        } else if settings.cache_experts > 0 {
                            format!("{} experts", settings.cache_experts)
                        } else {
                            "auto".to_owned()
                        };
                        format!(
                            "Engine: streaming {} • cold {} • cache {} • full layers {}{} • preload {}",
                            if settings.enabled { "on" } else { "off" },
                            if settings.cold { "on" } else { "off" },
                            cache,
                            settings.full_layers,
                            if settings.full_layers_set { " explicit" } else { " auto" },
                            if settings.preload_experts == 0 { "auto".to_owned() } else { settings.preload_experts.to_string() },
                        )
                    },
                ))
                .size(12),
            ]
            .spacing(10),
        );
        let steering_group = preference_group(
            "STEERING & DIAGNOSTICS",
            column![
                text("DIRECTIONAL STEERING").size(11).color(muted_text()),
                text("Direction-vector file").size(13),
                steering_file.padding(9),
                preference_input_row("FFN scale", steering_ffn),
                preference_input_row("Attention scale", steering_attn),
                text(if self.preference_draft.model == ModelChoice::Glm52 {
                    "Directional steering is not supported for GLM 5.2."
                } else {
                    "With a file and no explicit scale, DS4 defaults the FFN scale to 1. Scales accept -100 through 100."
                })
                .size(12),
                text(engine.as_ref().map_or_else(
                    || "Effective steering settings will appear after valid values are entered."
                        .to_owned(),
                    |engine| format!(
                        "Engine: file {} • FFN scale {} • attention scale {}",
                        if engine.steering.file.is_some() { "set" } else { "off" },
                        engine.steering.ffn_scale,
                        engine.steering.attention_scale,
                    ),
                ))
                .size(12),
                Space::with_height(6),
                text("ADVANCED DIAGNOSTICS").size(11).color(muted_text()),
                preference_input_row(
                    "Simulated used memory (GiB)",
                    text_input("Disabled", &self.preference_draft.simulated_used_memory_gib)
                        .on_input(Message::PreferenceSimulatedMemoryChanged),
                ),
                text("Routed expert profile output").size(13),
                text_input("Output file path", &self.preference_draft.expert_profile_path)
                    .on_input(Message::PreferenceExpertProfileChanged)
                    .padding(9),
                text(engine.as_ref().map_or_else(
                    || "Effective diagnostic settings will appear after valid values are entered."
                        .to_owned(),
                    |engine| format!(
                        "{} load: simulated memory {} • expert profile {}",
                        engine.model,
                        if engine.diagnostics.simulated_used_memory_bytes == 0 {
                            "off".to_owned()
                        } else {
                            format!("{} GiB", engine.diagnostics.simulated_used_memory_bytes / GIB)
                        },
                        if engine.diagnostics.expert_profile_path.is_some() { "set" } else { "off" },
                    ),
                ))
                .size(12),
            ]
            .spacing(10),
        );
        let mut fields = column![
            model_group,
            endpoint_group,
            generation_group,
            execution_group,
            acceleration_group,
            steering_group,
        ]
        .spacing(12);

        if let Some(error) = &self.preference_error {
            fields = fields.push(text(error).style(iced::widget::text::danger));
        }
        let header = row![
            icon(ICON_SETTINGS, 22),
            text("Preferences").size(24),
            Space::with_width(Length::Fill),
            text("⌘,").size(12),
        ]
        .spacing(10)
        .align_y(Alignment::Center);
        let footer = row![
            action_button("Reset DS4 defaults").on_press(Message::ResetPreferences),
            Space::with_width(Length::Fill),
            action_button("Cancel").on_press(Message::DismissPanel),
            action_button("Save").on_press(Message::SavePreferences),
        ]
        .spacing(8);

        let panel = container(
            column![
                header,
                scrollable(container(fields).padding(iced::Padding::ZERO.right(18)))
                    .height(Length::Fill),
                footer
            ]
            .spacing(16),
        )
        .padding(24)
        .width(700)
        .height(Length::Fill)
        .max_height(660)
        .style(overview_style);
        opaque(
            container(panel)
                .padding(24)
                .center_x(Length::Fill)
                .center_y(Length::Fill)
                .style(|_| {
                    container::Style::default().background(Color::from_rgba8(0, 0, 0, 0.68))
                }),
        )
    }
}
