use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

use crate::config::ProviderSource;
use crate::provider_setup::{
    ConnectDialog, EditModelStep, EditProviderDraft, EditProviderStep, NewProviderDraft,
    NewProviderStep,
};

use super::App;

#[derive(Clone, Debug)]
pub(crate) enum ProviderPickerItem {
    Provider {
        provider_id: String,
        display_name: String,
        source: ProviderSource,
        connected: bool,
    },
    AddNew {
        query: String,
    },
}

impl App {
    pub(crate) fn open_connect_dialog(&mut self) -> Result<()> {
        self.command_palette.clear();
        self.at_mention.clear();
        self.draft_attachments.clear();
        self.mcp_panel = None;

        self.composer.clear();
        self.composer
            .set_placeholder("Search providers by id or display name");
        self.connect_dialog = Some(ConnectDialog::provider_picker());

        Ok(())
    }

    pub(crate) fn provider_picker_items(&self) -> Vec<ProviderPickerItem> {
        let query = self.composer.text().trim().to_ascii_lowercase();

        let mut items = self
            .config
            .provider_ids()
            .into_iter()
            .filter_map(|provider_id| {
                let display_name = self
                    .config
                    .provider_display_name(&provider_id)
                    .unwrap_or(&provider_id)
                    .to_string();
                let source = self
                    .config
                    .provider_source(&provider_id)
                    .unwrap_or(ProviderSource::User);
                let connected = self.auth.api_key(&provider_id).is_some();

                if provider_picker_matches(&query, &provider_id, &display_name) {
                    Some(ProviderPickerItem::Provider {
                        provider_id,
                        display_name,
                        source,
                        connected,
                    })
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        items.push(ProviderPickerItem::AddNew {
            query: self.composer.text().trim().to_string(),
        });

        items
    }

    fn begin_connect_key_entry(&mut self, provider_id: String) {
        let label = self
            .config
            .provider_display_name(&provider_id)
            .map(str::to_string)
            .unwrap_or_else(|| provider_id.clone());
        self.at_mention.clear();
        self.draft_attachments.clear();
        self.composer.clear();
        self.composer
            .set_placeholder(format!("Enter API key for {label}"));
        self.connect_dialog = Some(ConnectDialog::ApiKey { provider_id });
    }

    fn begin_provider_edit(&mut self, provider_id: String) -> Result<()> {
        self.begin_provider_edit_with_selection(provider_id, None)
    }

    pub(crate) fn begin_provider_edit_for_model(
        &mut self,
        provider_id: String,
        model_id: String,
    ) -> Result<()> {
        self.begin_provider_edit_with_selection(provider_id, Some(model_id))
    }

    fn begin_provider_edit_with_selection(
        &mut self,
        provider_id: String,
        selected_model_id: Option<String>,
    ) -> Result<()> {
        let Some(provider) = self.config.providers.get(&provider_id).cloned() else {
            self.last_notice = Some(format!("Provider '{provider_id}' is not editable"));
            return Ok(());
        };

        let draft = EditProviderDraft::from_provider(
            &provider,
            self.auth.api_key(&provider_id).map(str::to_string),
        );
        if let Some(model_id) = selected_model_id
            && let Some(index) = draft
                .models
                .keys()
                .position(|candidate| candidate == &model_id)
        {
            let mut draft = draft;
            draft.selected_model_index = index;
            self.show_edit_provider_step(provider_id, EditProviderStep::ModelList, None, draft);
            return Ok(());
        }
        self.show_edit_provider_step(provider_id, EditProviderStep::DisplayName, None, draft);
        Ok(())
    }

    fn show_edit_provider_step(
        &mut self,
        provider_id: String,
        step: EditProviderStep,
        model_step: Option<EditModelStep>,
        draft: EditProviderDraft,
    ) {
        self.at_mention.clear();
        self.draft_attachments.clear();
        self.composer.clear();
        if let Some(model_step) = model_step {
            self.composer.set_placeholder(format!(
                "{} · {}",
                model_step.label(),
                model_step.help()
            ));
            self.composer
                .set_text(draft.model.current_value_for_edit(model_step));
        } else if step == EditProviderStep::ModelList {
            self.composer
                .set_placeholder("Enter to edit · n to add · d to delete · s to save");
        } else if step == EditProviderStep::ConfirmDeleteModel {
            self.composer.set_placeholder("y or n");
        } else {
            self.composer
                .set_placeholder(format!("{} · {}", step.label(), step.help()));
            self.composer.set_text(draft.current_value(step));
        }

        self.connect_dialog = Some(ConnectDialog::EditProvider {
            provider_id,
            step,
            model_step,
            draft,
        });
    }

    fn begin_new_model_in_edit(&mut self, provider_id: String, mut draft: EditProviderDraft) {
        draft.begin_new_model();
        self.show_edit_provider_step(
            provider_id,
            EditProviderStep::ModelList,
            Some(EditModelStep::ModelId),
            draft,
        );
    }

    fn begin_existing_model_edit(
        &mut self,
        provider_id: String,
        model_id: String,
        mut draft: EditProviderDraft,
    ) -> Result<()> {
        draft.begin_edit_model(&model_id)?;
        self.show_edit_provider_step(
            provider_id,
            EditProviderStep::ModelList,
            Some(EditModelStep::ModelDisplayName),
            draft,
        );
        Ok(())
    }

    fn finish_provider_edit(
        &mut self,
        provider_id: String,
        draft: EditProviderDraft,
    ) -> Result<()> {
        let (provider_config, api_key) = draft.into_provider_config()?;
        self.config
            .providers
            .insert(provider_id.clone(), provider_config);
        self.config.save(&self.paths)?;

        self.auth.set_api_key(provider_id.clone(), api_key);
        self.auth.save(&self.paths)?;

        self.refresh_active_model_after_provider_change(&provider_id)?;

        self.cancel_connect_dialog();
        self.last_notice = Some(format!("Updated provider '{provider_id}'"));
        Ok(())
    }

    fn refresh_active_model_after_provider_change(&mut self, provider_id: &str) -> Result<()> {
        if self.active_model.provider_id != provider_id {
            return Ok(());
        }

        let updated = self
            .config
            .resolve_model_by_ids(
                &self.auth,
                &self.active_model.provider_id,
                &self.active_model.model_id,
            )
            .or_else(|_| {
                self.config
                    .resolve_provider_default_model(&self.auth, provider_id)
            })?;

        self.active_model = updated.clone();
        self.conversation.set_model(
            updated.provider_id.clone(),
            updated.provider_display_name.clone(),
            updated.model_id.clone(),
            updated.display_name.clone(),
        );
        self.store.update_session_model(
            self.conversation.session_id,
            &updated.provider_id,
            &updated.provider_display_name,
            &updated.model_id,
            &updated.display_name,
        )?;

        Ok(())
    }

    fn finish_connect_api_key(&mut self, provider_id: String, api_key: String) -> Result<()> {
        if api_key.trim().is_empty() {
            self.cancel_connect_dialog();
            self.last_notice = Some("API key was empty".to_string());
            return Ok(());
        }

        self.auth.set_api_key(provider_id.clone(), api_key);
        self.auth.save(&self.paths)?;

        let model = self
            .config
            .resolve_provider_default_model(&self.auth, &provider_id)?;

        self.active_model = model.clone();
        self.conversation.set_model(
            model.provider_id.clone(),
            model.provider_display_name.clone(),
            model.model_id.clone(),
            model.display_name.clone(),
        );

        if self
            .store
            .load_session_record(self.conversation.session_id)?
            .is_some()
        {
            self.store.update_session_model(
                self.conversation.session_id,
                &model.provider_id,
                &model.provider_display_name,
                &model.model_id,
                &model.display_name,
            )?;
        }

        self.cancel_connect_dialog();
        self.last_notice = Some(format!("Connected to {}", model.provider_display_name));
        Ok(())
    }

    fn finish_new_provider_setup(&mut self, draft: NewProviderDraft) -> Result<()> {
        let provider_id = draft.provider_id.clone();

        if self.config.provider_exists(&provider_id) {
            self.last_notice = Some(format!("Provider '{provider_id}' already exists"));
            self.show_new_provider_step(NewProviderStep::ProviderId, draft);
            return Ok(());
        }

        let (provider_id, provider_config, api_key) = draft.into_provider_config()?;

        self.config
            .providers
            .insert(provider_id.clone(), provider_config);
        self.config.save(&self.paths)?;

        self.auth.set_api_key(provider_id.clone(), api_key);
        self.auth.save(&self.paths)?;

        let model = self
            .config
            .resolve_provider_default_model(&self.auth, &provider_id)?;

        self.active_model = model.clone();
        self.conversation.set_model(
            model.provider_id.clone(),
            model.provider_display_name.clone(),
            model.model_id.clone(),
            model.display_name.clone(),
        );

        if self
            .store
            .load_session_record(self.conversation.session_id)?
            .is_some()
        {
            self.store.update_session_model(
                self.conversation.session_id,
                &model.provider_id,
                &model.provider_display_name,
                &model.model_id,
                &model.display_name,
            )?;
        }

        self.cancel_connect_dialog();
        self.last_notice = Some(format!(
            "Created provider '{provider_id}' and connected to {}",
            model.provider_display_name
        ));
        Ok(())
    }

    fn begin_new_provider_setup(&mut self) {
        self.show_new_provider_step(NewProviderStep::ProviderId, NewProviderDraft::default());
    }

    fn show_new_provider_step(&mut self, step: NewProviderStep, draft: NewProviderDraft) {
        self.at_mention.clear();
        self.draft_attachments.clear();
        self.composer.clear();
        self.composer
            .set_placeholder(format!("{} · {}", step.label(), step.help()));
        self.composer.set_text(draft.current_value(step));
        self.connect_dialog = Some(ConnectDialog::NewProvider { step, draft });
    }

    fn cancel_connect_dialog(&mut self) {
        self.connect_dialog = None;
        self.at_mention.clear();
        self.draft_attachments.clear();
        self.composer.clear();
        self.composer
            .set_placeholder("Ask TiDev about your code, task, or question...");
    }

    pub(crate) fn handle_connect_dialog_key(
        &mut self,
        key: KeyEvent,
        dialog: ConnectDialog,
    ) -> Result<()> {
        match dialog {
            ConnectDialog::ProviderPicker { selected } => match key {
                KeyEvent {
                    code: KeyCode::Esc, ..
                } => {
                    self.cancel_connect_dialog();
                }
                KeyEvent {
                    code: KeyCode::Enter,
                    modifiers,
                    ..
                } if !modifiers.contains(KeyModifiers::SHIFT)
                    && !modifiers.contains(KeyModifiers::ALT) =>
                {
                    let items = self.provider_picker_items();
                    if items.is_empty() {
                        return Ok(());
                    }

                    let selected = selected.min(items.len().saturating_sub(1));
                    match items.get(selected) {
                        Some(ProviderPickerItem::Provider { provider_id, .. }) => {
                            self.begin_connect_key_entry(provider_id.clone());
                        }
                        Some(ProviderPickerItem::AddNew { .. }) => {
                            self.begin_new_provider_setup();
                        }
                        None => {}
                    }
                }
                KeyEvent {
                    code: KeyCode::Up, ..
                } => {
                    let item_count = self.provider_picker_items().len();
                    let current = selected.min(item_count.saturating_sub(1));
                    let next = if current == 0 {
                        item_count.saturating_sub(1)
                    } else {
                        current - 1
                    };
                    self.connect_dialog = Some(ConnectDialog::ProviderPicker { selected: next });
                }
                KeyEvent {
                    code: KeyCode::Down,
                    ..
                } => {
                    let item_count = self.provider_picker_items().len();
                    let current = selected.min(item_count.saturating_sub(1));
                    let next = if item_count == 0 {
                        0
                    } else {
                        (current + 1) % item_count
                    };
                    self.connect_dialog = Some(ConnectDialog::ProviderPicker { selected: next });
                }
                KeyEvent {
                    code: KeyCode::Tab, ..
                } => {}
                KeyEvent {
                    code: KeyCode::Char('e'),
                    modifiers,
                    ..
                } if modifiers.contains(KeyModifiers::CONTROL) => {
                    let items = self.provider_picker_items();
                    let selected = selected.min(items.len().saturating_sub(1));
                    if let Some(ProviderPickerItem::Provider {
                        provider_id,
                        source,
                        ..
                    }) = items.get(selected)
                    {
                        if matches!(source, ProviderSource::User) {
                            self.begin_provider_edit(provider_id.clone())?;
                        } else {
                            self.last_notice = Some(
                                "Bundled presets are read-only; clone them with the new provider flow".to_string(),
                            );
                        }
                    }
                }
                _ => {
                    let previous_query = self.composer.text().to_string();
                    let _ = self.composer.handle_key_with_history(key, false);

                    if self.composer.text() != previous_query {
                        self.connect_dialog = Some(ConnectDialog::ProviderPicker { selected: 0 });
                    }
                }
            },
            ConnectDialog::ApiKey { provider_id } => {
                if matches!(key.code, KeyCode::Esc) {
                    self.cancel_connect_dialog();
                    return Ok(());
                }

                if let Some(submission) = self.composer.handle_key_with_history(key, false) {
                    self.finish_connect_api_key(provider_id, submission)?;
                }
            }
            ConnectDialog::NewProvider { step, mut draft } => {
                if matches!(key.code, KeyCode::Esc) {
                    self.cancel_connect_dialog();
                    return Ok(());
                }

                if step == NewProviderStep::AddAnotherModel {
                    if let Some(submission) = self.composer.handle_key_with_history(key, false) {
                        match parse_add_another_model_answer(&submission) {
                            Some(true) => {
                                self.show_new_provider_step(NewProviderStep::ModelId, draft);
                            }
                            Some(false) => {
                                self.finish_new_provider_setup(draft)?;
                            }
                            None => {
                                self.last_notice = Some("Enter y or n".to_string());
                                self.show_new_provider_step(step, draft);
                            }
                        }
                    } else if matches!(key.code, KeyCode::Enter)
                        && !key.modifiers.contains(KeyModifiers::SHIFT)
                        && !key.modifiers.contains(KeyModifiers::ALT)
                    {
                        self.finish_new_provider_setup(draft)?;
                    }

                    return Ok(());
                }

                if let Some(submission) = self.composer.handle_key_with_history(key, false) {
                    if let Err(error) = draft.apply_step(step, &submission) {
                        self.last_notice = Some(error.to_string());
                        self.show_new_provider_step(step, draft);
                        return Ok(());
                    }

                    if step == NewProviderStep::ProviderId
                        && self.config.providers.contains_key(&draft.provider_id)
                    {
                        self.last_notice =
                            Some(format!("Provider '{}' already exists", draft.provider_id));
                        self.show_new_provider_step(NewProviderStep::ProviderId, draft);
                        return Ok(());
                    }

                    if step == NewProviderStep::ModelId
                        && draft.models.contains_key(&draft.model.model_id)
                    {
                        self.last_notice =
                            Some(format!("Model '{}' already exists", draft.model.model_id));
                        self.show_new_provider_step(NewProviderStep::ModelId, draft);
                        return Ok(());
                    }

                    if step == NewProviderStep::Temperature {
                        if let Err(error) = draft.finish_current_model() {
                            self.last_notice = Some(error.to_string());
                            self.show_new_provider_step(NewProviderStep::ModelId, draft);
                            return Ok(());
                        }

                        self.show_new_provider_step(NewProviderStep::AddAnotherModel, draft);
                        return Ok(());
                    }

                    if let Some(next_step) = step.next() {
                        self.show_new_provider_step(next_step, draft);
                    } else {
                        self.finish_new_provider_setup(draft)?;
                    }
                }
            }
            ConnectDialog::EditProvider {
                provider_id,
                step,
                model_step,
                mut draft,
            } => {
                if matches!(key.code, KeyCode::Esc) {
                    self.cancel_connect_dialog();
                    return Ok(());
                }

                if let Some(model_step) = model_step {
                    if let Some(submission) = self.composer.handle_key_with_history(key, false) {
                        if let Err(error) = draft.model.apply_edit_step(model_step, &submission) {
                            self.last_notice = Some(error.to_string());
                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                Some(model_step),
                                draft,
                            );
                            return Ok(());
                        }

                        if let Some(next_step) = model_step.next(draft.editing_model_id.is_some()) {
                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                Some(next_step),
                                draft,
                            );
                        } else {
                            if let Err(error) = draft.finish_current_model() {
                                self.last_notice = Some(error.to_string());
                                self.show_edit_provider_step(
                                    provider_id,
                                    EditProviderStep::ModelList,
                                    Some(EditModelStep::ModelId),
                                    draft,
                                );
                                return Ok(());
                            }

                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                None,
                                draft,
                            );
                        }
                    }

                    return Ok(());
                }

                match step {
                    EditProviderStep::DisplayName
                    | EditProviderStep::BaseUrl
                    | EditProviderStep::ApiKey => {
                        if let Some(submission) = self.composer.handle_key_with_history(key, false)
                        {
                            if let Err(error) = draft.apply_step(step, &submission) {
                                self.last_notice = Some(error.to_string());
                                self.show_edit_provider_step(provider_id, step, None, draft);
                                return Ok(());
                            }

                            if let Some(next_step) = step.next() {
                                self.show_edit_provider_step(provider_id, next_step, None, draft);
                            } else {
                                self.show_edit_provider_step(
                                    provider_id,
                                    EditProviderStep::ModelList,
                                    None,
                                    draft,
                                );
                            }
                        }
                    }
                    EditProviderStep::ModelList => match key {
                        KeyEvent {
                            code: KeyCode::Up, ..
                        } => {
                            draft.move_selection_up();
                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                None,
                                draft,
                            );
                        }
                        KeyEvent {
                            code: KeyCode::Down,
                            ..
                        } => {
                            draft.move_selection_down();
                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                None,
                                draft,
                            );
                        }
                        KeyEvent {
                            code: KeyCode::Enter,
                            modifiers,
                            ..
                        } if !modifiers.contains(KeyModifiers::SHIFT)
                            && !modifiers.contains(KeyModifiers::ALT) =>
                        {
                            if let Some(model_id) = draft.selected_model_id() {
                                self.begin_existing_model_edit(provider_id, model_id, draft)?;
                            }
                        }
                        KeyEvent {
                            code: KeyCode::Char('n'),
                            modifiers,
                            ..
                        } if !modifiers.contains(KeyModifiers::CONTROL)
                            && !modifiers.contains(KeyModifiers::ALT) =>
                        {
                            self.begin_new_model_in_edit(provider_id, draft);
                        }
                        KeyEvent {
                            code: KeyCode::Char('d'),
                            modifiers,
                            ..
                        } if !modifiers.contains(KeyModifiers::CONTROL)
                            && !modifiers.contains(KeyModifiers::ALT) =>
                        {
                            if let Err(error) = draft.request_delete_selected_model() {
                                self.last_notice = Some(error.to_string());
                                self.show_edit_provider_step(
                                    provider_id,
                                    EditProviderStep::ModelList,
                                    None,
                                    draft,
                                );
                            } else {
                                self.show_edit_provider_step(
                                    provider_id,
                                    EditProviderStep::ConfirmDeleteModel,
                                    None,
                                    draft,
                                );
                            }
                        }
                        KeyEvent {
                            code: KeyCode::Char('s'),
                            modifiers,
                            ..
                        } if !modifiers.contains(KeyModifiers::CONTROL)
                            && !modifiers.contains(KeyModifiers::ALT) =>
                        {
                            self.finish_provider_edit(provider_id, draft)?;
                        }
                        _ => {}
                    },
                    EditProviderStep::ConfirmDeleteModel => {
                        if let Some(submission) = self.composer.handle_key_with_history(key, false)
                        {
                            match parse_add_another_model_answer(&submission) {
                                Some(true) => {
                                    if let Err(error) = draft.confirm_delete_selected_model() {
                                        self.last_notice = Some(error.to_string());
                                    }
                                    self.show_edit_provider_step(
                                        provider_id,
                                        EditProviderStep::ModelList,
                                        None,
                                        draft,
                                    );
                                }
                                Some(false) => {
                                    draft.pending_delete_model_id = None;
                                    self.show_edit_provider_step(
                                        provider_id,
                                        EditProviderStep::ModelList,
                                        None,
                                        draft,
                                    );
                                }
                                None => {
                                    self.last_notice = Some("Enter y or n".to_string());
                                    self.show_edit_provider_step(
                                        provider_id,
                                        EditProviderStep::ConfirmDeleteModel,
                                        None,
                                        draft,
                                    );
                                }
                            }
                        } else if matches!(key.code, KeyCode::Enter)
                            && !key.modifiers.contains(KeyModifiers::SHIFT)
                            && !key.modifiers.contains(KeyModifiers::ALT)
                        {
                            if let Err(error) = draft.confirm_delete_selected_model() {
                                self.last_notice = Some(error.to_string());
                            }
                            self.show_edit_provider_step(
                                provider_id,
                                EditProviderStep::ModelList,
                                None,
                                draft,
                            );
                        }
                    }
                }
            }
        }

        Ok(())
    }
}

fn parse_add_another_model_answer(input: &str) -> Option<bool> {
    let answer = input.trim().to_ascii_lowercase();

    match answer.as_str() {
        "y" | "yes" | "true" | "1" => Some(true),
        "n" | "no" | "false" | "0" => Some(false),
        _ if answer.is_empty() => Some(false),
        _ => None,
    }
}

fn provider_picker_matches(query: &str, provider_id: &str, display_name: &str) -> bool {
    if query.is_empty() {
        return true;
    }

    let provider_id = provider_id.to_ascii_lowercase();
    let display_name = display_name.to_ascii_lowercase();
    provider_id.contains(query) || display_name.contains(query)
}
