use crate::chat::{ModelInfo, ProviderInfo};

#[derive(Debug, Clone)]
pub struct ProviderDialog {
    pub providers: Vec<ProviderInfo>,
    pub selected_provider: usize,
    pub selected_model: usize,
    pub focus: DialogFocus,
    pub local_models_loading: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DialogFocus {
    Providers,
    Models,
}

impl ProviderDialog {
    pub fn new(providers: Vec<ProviderInfo>) -> Self {
        Self {
            providers,
            selected_provider: 0,
            selected_model: 0,
            focus: DialogFocus::Providers,
            local_models_loading: false,
        }
    }

    pub fn new_with_selection(
        providers: Vec<ProviderInfo>,
        provider_name: Option<&str>,
        model_id: Option<&str>,
    ) -> Self {
        let mut dialog = Self::new(providers);

        let Some(provider_name) = provider_name.filter(|name| !name.is_empty()) else {
            return dialog;
        };

        if let Some(provider_index) = dialog
            .providers
            .iter()
            .position(|provider| provider.name.eq_ignore_ascii_case(provider_name))
        {
            dialog.selected_provider = provider_index;
            if let Some(model_id) = model_id.filter(|id| !id.is_empty()) {
                if let Some(model_index) = dialog.providers[provider_index]
                    .models
                    .iter()
                    .position(|model| model.id.eq_ignore_ascii_case(model_id))
                {
                    dialog.selected_model = model_index;
                }
            }
        }

        dialog
    }

    pub fn current_models(&self) -> Vec<ModelInfo> {
        self.providers
            .get(self.selected_provider)
            .map(|p| p.models.clone())
            .unwrap_or_default()
    }

    pub fn move_up(&mut self) {
        match self.focus {
            DialogFocus::Providers => {
                if self.selected_provider > 0 {
                    self.selected_provider -= 1;
                    self.selected_model = 0;
                }
            }
            DialogFocus::Models => {
                if self.selected_model > 0 {
                    self.selected_model -= 1;
                }
            }
        }
    }

    pub fn move_down(&mut self) {
        match self.focus {
            DialogFocus::Providers => {
                if self.selected_provider < self.providers.len().saturating_sub(1) {
                    self.selected_provider += 1;
                    self.selected_model = 0;
                }
            }
            DialogFocus::Models => {
                let models = self.current_models();
                if self.selected_model < models.len().saturating_sub(1) {
                    self.selected_model += 1;
                }
            }
        }
    }

    pub fn switch_to_providers(&mut self) {
        self.focus = DialogFocus::Providers;
    }

    pub fn switch_to_models(&mut self) {
        self.focus = DialogFocus::Models;
    }

    pub fn selected(&self) -> Option<(String, String)> {
        let provider = self.providers.get(self.selected_provider)?;
        let model = provider.models.get(self.selected_model)?;
        Some((provider.name.clone(), model.id.clone()))
    }

    pub fn set_local_models_loading(&mut self) {
        self.local_models_loading = true;
    }

    pub fn apply_fetched_local_models(&mut self, models: Vec<ModelInfo>) {
        self.local_models_loading = false;
        if let Some(provider) = self.providers.iter_mut().find(|p| p.name == "local") {
            if !models.is_empty() {
                provider.models = models;
            } else {
                provider.models = vec![ModelInfo {
                    id: "glm4.7".to_string(),
                    name: "glm4.7".to_string(),
                }];
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn providers() -> Vec<ProviderInfo> {
        vec![
            ProviderInfo {
                name: "openai".to_string(),
                models: vec![
                    ModelInfo {
                        id: "gpt-4o".to_string(),
                        name: "GPT-4o".to_string(),
                    },
                    ModelInfo {
                        id: "gpt-4-turbo".to_string(),
                        name: "GPT-4 Turbo".to_string(),
                    },
                ],
            },
            ProviderInfo {
                name: "deepseek".to_string(),
                models: vec![
                    ModelInfo {
                        id: "deepseek-v4-flash".to_string(),
                        name: "DeepSeek V4 Flash".to_string(),
                    },
                    ModelInfo {
                        id: "deepseek-v4-pro".to_string(),
                        name: "DeepSeek V4 Pro".to_string(),
                    },
                    ModelInfo {
                        id: "deepseek-chat".to_string(),
                        name: "DeepSeek Chat V3".to_string(),
                    },
                ],
            },
        ]
    }

    #[test]
    fn new_with_selection_preselects_matching_provider_and_model() {
        let dialog =
            ProviderDialog::new_with_selection(providers(), Some("openai"), Some("gpt-4-turbo"));

        assert_eq!(dialog.selected_provider, 0);
        assert_eq!(dialog.selected_model, 1);
        assert_eq!(
            dialog.selected(),
            Some(("openai".to_string(), "gpt-4-turbo".to_string()))
        );
    }

    #[test]
    fn new_with_selection_falls_back_to_first_model_when_model_missing() {
        let dialog = ProviderDialog::new_with_selection(
            providers(),
            Some("deepseek"),
            Some("missing-model"),
        );

        assert_eq!(dialog.selected_provider, 1);
        assert_eq!(dialog.selected_model, 0);
        assert_eq!(
            dialog.selected(),
            Some(("deepseek".to_string(), "deepseek-v4-flash".to_string()))
        );
    }
}