use core::num;

use gpui::App;
use language::CursorShape;
use project::project_settings::DiagnosticSeverity;
pub use settings::{
    CodeLens, CompletionDetailAlignment, CompletionMenuItemKind, CurrentLineHighlight, DelayMs,
    DiffViewStyle, DisplayIn, DocumentColorsRenderMode, DoubleClickInMultibuffer,
    GoToDefinitionFallback, GoToDefinitionScrollStrategy, MinimapThumb, MinimapThumbBorder,
    MultiCursorModifier, ScrollBeyondLastLine, ScrollbarDiagnostics, SeedQuerySetting, ShowMinimap,
    SnippetSortOrder,
};
use settings::{RegisterSetting, RelativeLineNumbers, Settings};
use ui::scrollbars::ShowScrollbar;

/// Imports from the VSCode settings at
/// https://code.visualstudio.com/docs/reference/default-settings
#[derive(Clone, RegisterSetting)]
pub struct EditorSettings {
    pub cursor_blink: bool,
    pub cursor_shape: Option<CursorShape>,
    pub current_line_highlight: CurrentLineHighlight,
    pub selection_highlight: bool,
    pub rounded_selection: bool,
    pub lsp_highlight_debounce: DelayMs,
    pub hover_popover_enabled: bool,
    pub hover_popover_delay: DelayMs,
    pub hover_popover_sticky: bool,
    pub hover_popover_hiding_delay: DelayMs,
    pub toolbar: Toolbar,
    pub scrollbar: Scrollbar,
    pub minimap: Minimap,
    pub gutter: Gutter,
    pub scroll_beyond_last_line: ScrollBeyondLastLine,
    pub vertical_scroll_margin: f64,
    pub autoscroll_on_clicks: bool,
    pub horizontal_scroll_margin: f32,
    pub scroll_sensitivity: f32,
    pub mouse_wheel_zoom: bool,
    pub fast_scroll_sensitivity: f32,
    pub sticky_scroll: StickyScroll,
    pub relative_line_numbers: RelativeLineNumbers,
    pub seed_search_query_from_cursor: SeedQuerySetting,
    pub use_smartcase_search: bool,
    pub multi_cursor_modifier: MultiCursorModifier,
    pub redact_private_values: bool,
    pub expand_excerpt_lines: u32,
    pub excerpt_context_lines: u32,
    pub middle_click_paste: bool,
    pub double_click_in_multibuffer: DoubleClickInMultibuffer,
    pub search_wrap: bool,
    pub search: SearchSettings,
    pub auto_signature_help: bool,
    pub show_signature_help_after_edits: bool,
    pub go_to_definition_fallback: GoToDefinitionFallback,
    pub go_to_definition_scroll_strategy: GoToDefinitionScrollStrategy,
    pub jupyter: Jupyter,
    pub snippet_sort_order: SnippetSortOrder,
    pub diagnostics_max_severity: Option<DiagnosticSeverity>,
    pub inline_code_actions: bool,
    pub drag_and_drop_selection: DragAndDropSelection,
    pub code_lens: CodeLens,
    pub lsp_document_colors: DocumentColorsRenderMode,
    pub lsp_document_links: bool,
    pub minimum_contrast_for_highlights: f32,
    pub completion_menu_scrollbar: ShowScrollbar,
    pub completion_detail_alignment: CompletionDetailAlignment,
    pub completion_menu_item_kind: CompletionMenuItemKind,
    pub diff_view_style: DiffViewStyle,
    pub minimum_split_diff_width: f32,
}
#[derive(Debug, Clone)]
pub struct Jupyter {
    /// Whether the Jupyter feature is enabled.
    ///
    /// Default: true
    pub enabled: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct StickyScroll {
    pub enabled: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Toolbar {
    pub breadcrumbs: bool,
    pub quick_actions: bool,
    pub selections_menu: bool,
    pub agent_review: bool,
    pub code_actions: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Scrollbar {
    pub show: ShowScrollbar,
    pub git_diff: bool,
    pub selected_text: bool,
    pub selected_symbol: bool,
    pub search_results: bool,
    pub diagnostics: ScrollbarDiagnostics,
    pub cursors: bool,
    pub axes: ScrollbarAxes,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Minimap {
    pub show: ShowMinimap,
    pub display_in: DisplayIn,
    pub thumb: MinimapThumb,
    pub thumb_border: MinimapThumbBorder,
    pub current_line_highlight: Option<CurrentLineHighlight>,
    pub max_width_columns: num::NonZeroU32,
}

impl Minimap {
    pub fn minimap_enabled(&self) -> bool {
        self.show != ShowMinimap::Never
    }

    #[inline]
    pub fn on_active_editor(&self) -> bool {
        self.display_in == DisplayIn::ActiveEditor
    }

    pub fn with_show_override(self) -> Self {
        Self {
            show: ShowMinimap::Always,
            ..self
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Gutter {
    pub min_line_number_digits: usize,
    pub line_numbers: bool,
    pub runnables: bool,
    pub breakpoints: bool,
    pub bookmarks: bool,
    pub folds: bool,
}

/// Forcefully enable or disable the scrollbar for each axis
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ScrollbarAxes {
    /// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
    ///
    /// Default: true
    pub horizontal: bool,

    /// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
    ///
    /// Default: true
    pub vertical: bool,
}

/// Whether to allow drag and drop text selection in buffer.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct DragAndDropSelection {
    /// When true, enables drag and drop text selection in buffer.
    ///
    /// Default: true
    pub enabled: bool,

    /// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created.
    ///
    /// Default: 300
    pub delay: DelayMs,
}

/// Default options for buffer and project search items.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct SearchSettings {
    /// Whether to show the project search button in the status bar.
    pub button: bool,
    /// Whether to only match on whole words.
    pub whole_word: bool,
    /// Whether to match case sensitively.
    pub case_sensitive: bool,
    /// Whether to include gitignored files in search results.
    pub include_ignored: bool,
    /// Whether to interpret the search query as a regular expression.
    pub regex: bool,
    /// Whether to center the cursor on each search match when navigating.
    pub center_on_match: bool,
}

impl EditorSettings {
    pub fn jupyter_enabled(cx: &App) -> bool {
        EditorSettings::get_global(cx).jupyter.enabled
    }
}

impl Settings for EditorSettings {
    fn from_settings(content: &settings::SettingsContent) -> Self {
        let editor = content.editor.clone();
        let scrollbar = editor.scrollbar.unwrap();
        let minimap = editor.minimap.unwrap();
        let gutter = editor.gutter.unwrap();
        let axes = scrollbar.axes.unwrap();
        let toolbar = editor.toolbar.unwrap();
        let search = editor.search.unwrap();
        let drag_and_drop_selection = editor.drag_and_drop_selection.unwrap();
        let sticky_scroll = editor.sticky_scroll.unwrap();
        Self {
            cursor_blink: editor.cursor_blink.unwrap(),
            cursor_shape: editor.cursor_shape.map(Into::into),
            current_line_highlight: editor.current_line_highlight.unwrap(),
            selection_highlight: editor.selection_highlight.unwrap(),
            rounded_selection: editor.rounded_selection.unwrap(),
            lsp_highlight_debounce: editor.lsp_highlight_debounce.unwrap(),
            hover_popover_enabled: editor.hover_popover_enabled.unwrap(),
            hover_popover_delay: editor.hover_popover_delay.unwrap(),
            hover_popover_sticky: editor.hover_popover_sticky.unwrap(),
            hover_popover_hiding_delay: editor.hover_popover_hiding_delay.unwrap(),
            toolbar: Toolbar {
                breadcrumbs: toolbar.breadcrumbs.unwrap(),
                quick_actions: toolbar.quick_actions.unwrap(),
                selections_menu: toolbar.selections_menu.unwrap(),
                agent_review: toolbar.agent_review.unwrap(),
                code_actions: toolbar.code_actions.unwrap(),
            },
            scrollbar: Scrollbar {
                show: scrollbar.show.map(ui_scrollbar_settings_from_raw).unwrap(),
                git_diff: scrollbar.git_diff.unwrap()
                    && content
                        .git
                        .as_ref()
                        .unwrap()
                        .enabled
                        .unwrap()
                        .is_git_diff_enabled(),
                selected_text: scrollbar.selected_text.unwrap(),
                selected_symbol: scrollbar.selected_symbol.unwrap(),
                search_results: scrollbar.search_results.unwrap(),
                diagnostics: scrollbar.diagnostics.unwrap(),
                cursors: scrollbar.cursors.unwrap(),
                axes: ScrollbarAxes {
                    horizontal: axes.horizontal.unwrap(),
                    vertical: axes.vertical.unwrap(),
                },
            },
            minimap: Minimap {
                show: minimap.show.unwrap(),
                display_in: minimap.display_in.unwrap(),
                thumb: minimap.thumb.unwrap(),
                thumb_border: minimap.thumb_border.unwrap(),
                current_line_highlight: minimap.current_line_highlight,
                max_width_columns: minimap.max_width_columns.unwrap(),
            },
            gutter: Gutter {
                min_line_number_digits: gutter.min_line_number_digits.unwrap(),
                line_numbers: gutter.line_numbers.unwrap(),
                runnables: gutter.runnables.unwrap(),
                bookmarks: gutter.bookmarks.unwrap(),
                breakpoints: gutter.breakpoints.unwrap(),
                folds: gutter.folds.unwrap(),
            },
            scroll_beyond_last_line: editor.scroll_beyond_last_line.unwrap(),
            vertical_scroll_margin: editor.vertical_scroll_margin.unwrap() as f64,
            autoscroll_on_clicks: editor.autoscroll_on_clicks.unwrap(),
            horizontal_scroll_margin: editor.horizontal_scroll_margin.unwrap(),
            scroll_sensitivity: editor.scroll_sensitivity.unwrap(),
            mouse_wheel_zoom: editor.mouse_wheel_zoom.unwrap(),
            fast_scroll_sensitivity: editor.fast_scroll_sensitivity.unwrap(),
            sticky_scroll: StickyScroll {
                enabled: sticky_scroll.enabled.unwrap(),
            },
            relative_line_numbers: editor.relative_line_numbers.unwrap(),
            seed_search_query_from_cursor: editor.seed_search_query_from_cursor.unwrap(),
            use_smartcase_search: editor.use_smartcase_search.unwrap(),
            multi_cursor_modifier: editor.multi_cursor_modifier.unwrap(),
            redact_private_values: editor.redact_private_values.unwrap(),
            expand_excerpt_lines: editor.expand_excerpt_lines.unwrap(),
            excerpt_context_lines: editor.excerpt_context_lines.unwrap(),
            middle_click_paste: editor.middle_click_paste.unwrap(),
            double_click_in_multibuffer: editor.double_click_in_multibuffer.unwrap(),
            search_wrap: editor.search_wrap.unwrap(),
            search: SearchSettings {
                button: search.button.unwrap(),
                whole_word: search.whole_word.unwrap(),
                case_sensitive: search.case_sensitive.unwrap(),
                include_ignored: search.include_ignored.unwrap(),
                regex: search.regex.unwrap(),
                center_on_match: search.center_on_match.unwrap(),
            },
            auto_signature_help: editor.auto_signature_help.unwrap(),
            show_signature_help_after_edits: editor.show_signature_help_after_edits.unwrap(),
            go_to_definition_fallback: editor.go_to_definition_fallback.unwrap(),
            go_to_definition_scroll_strategy: editor.go_to_definition_scroll_strategy.unwrap(),
            jupyter: Jupyter {
                enabled: editor.jupyter.unwrap().enabled.unwrap(),
            },
            snippet_sort_order: editor.snippet_sort_order.unwrap(),
            diagnostics_max_severity: editor.diagnostics_max_severity.map(Into::into),
            inline_code_actions: editor.inline_code_actions.unwrap(),
            drag_and_drop_selection: DragAndDropSelection {
                enabled: drag_and_drop_selection.enabled.unwrap(),
                delay: drag_and_drop_selection.delay.unwrap(),
            },
            code_lens: editor.code_lens.unwrap(),
            lsp_document_colors: editor.lsp_document_colors.unwrap(),
            lsp_document_links: editor.lsp_document_links.unwrap(),
            minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap().0,
            completion_menu_scrollbar: editor
                .completion_menu_scrollbar
                .map(ui_scrollbar_settings_from_raw)
                .unwrap(),
            completion_detail_alignment: editor.completion_detail_alignment.unwrap(),
            completion_menu_item_kind: editor.completion_menu_item_kind.unwrap(),
            diff_view_style: editor.diff_view_style.unwrap(),
            minimum_split_diff_width: editor.minimum_split_diff_width.unwrap(),
        }
    }
}

#[derive(Default)]
pub struct EditorSettingsScrollbarProxy;

impl ui::scrollbars::ScrollbarVisibility for EditorSettingsScrollbarProxy {
    fn visibility(&self, cx: &App) -> ShowScrollbar {
        EditorSettings::get_global(cx).scrollbar.show
    }
}

pub fn ui_scrollbar_settings_from_raw(
    value: settings::ShowScrollbar,
) -> ui::scrollbars::ShowScrollbar {
    match value {
        settings::ShowScrollbar::Auto => ShowScrollbar::Auto,
        settings::ShowScrollbar::System => ShowScrollbar::System,
        settings::ShowScrollbar::Always => ShowScrollbar::Always,
        settings::ShowScrollbar::Never => ShowScrollbar::Never,
    }
}
