use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
use ratatui::buffer::Buffer;
use ratatui::layout::Constraint;
use ratatui::layout::Layout;
use ratatui::layout::Rect;
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::widgets::Block;
use ratatui::widgets::Widget;

use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender;
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
use crate::key_hint;
use crate::key_hint::KeyBindingListExt;
use crate::keymap::ListKeymap;
use crate::render::Insets;
use crate::render::RectExt as _;
use crate::render::renderable::ColumnRenderable;
use crate::render::renderable::Renderable;
use crate::style::user_message_style;

use super::CancellationEvent;
use super::bottom_pane_view::BottomPaneView;
use super::popup_consts::MAX_POPUP_ROWS;
use super::scroll_state::ScrollState;
use super::selection_popup_common::GenericDisplayRow;
use super::selection_popup_common::measure_rows_height;
use super::selection_popup_common::render_rows;

const MEMORIES_DOC_URL: &str = "https://developers.openai.com/codex/memories";

#[derive(Clone, Copy, PartialEq, Eq)]
enum MemoriesSetting {
    Use,
    Generate,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum MemoriesAction {
    Reset,
}

enum MemoriesMenuItem {
    Setting {
        setting: MemoriesSetting,
        name: &'static str,
        description: &'static str,
        enabled: bool,
    },
    Action {
        action: MemoriesAction,
        name: &'static str,
        description: &'static str,
    },
}

pub(crate) struct MemoriesSettingsView {
    items: Vec<MemoriesMenuItem>,
    state: ScrollState,
    reset_confirmation: Option<ScrollState>,
    complete: bool,
    app_event_tx: AppEventSender,
    docs_link: Line<'static>,
    keymap: ListKeymap,
}

impl MemoriesSettingsView {
    pub(crate) fn new(
        use_memories: bool,
        generate_memories: bool,
        app_event_tx: AppEventSender,
        keymap: ListKeymap,
    ) -> Self {
        let mut view = Self {
            items: vec![
                MemoriesMenuItem::Setting {
                    setting: MemoriesSetting::Use,
                    name: "Use memories",
                    description: "Use memories in the following threads. Applied at next thread.",
                    enabled: use_memories,
                },
                MemoriesMenuItem::Setting {
                    setting: MemoriesSetting::Generate,
                    name: "Generate memories",
                    description: "Generate memories from the following threads. Current thread included.",
                    enabled: generate_memories,
                },
                MemoriesMenuItem::Action {
                    action: MemoriesAction::Reset,
                    name: "Reset all memories",
                    description: "Clear local memory files and summaries. Existing threads stay intact.",
                },
            ],
            state: ScrollState::new(),
            reset_confirmation: None,
            complete: false,
            app_event_tx,
            docs_link: Line::from(vec![
                "Learn more: ".dim(),
                MEMORIES_DOC_URL.cyan().underlined(),
            ]),
            keymap,
        };
        view.initialize_selection();
        view
    }

    fn initialize_selection(&mut self) {
        self.state.selected_idx = (!self.items.is_empty()).then_some(0);
    }

    fn settings_header(&self) -> ColumnRenderable<'_> {
        let mut header = ColumnRenderable::new();
        header.push(Line::from("Memories".bold()));
        header.push(Line::from(
            "Choose how Codex uses and creates memories. Changes are saved to config.toml".dim(),
        ));
        header
    }

    fn reset_confirmation_header(&self) -> ColumnRenderable<'_> {
        let mut header = ColumnRenderable::new();
        header.push(Line::from("Reset all memories?".bold()));
        header.push(Line::from(
            "This clears local memory files and rollout summaries for the current Codex home."
                .dim(),
        ));
        header
    }

    fn active_state(&self) -> &ScrollState {
        self.reset_confirmation.as_ref().unwrap_or(&self.state)
    }

    fn active_state_mut(&mut self) -> &mut ScrollState {
        self.reset_confirmation.as_mut().unwrap_or(&mut self.state)
    }

    fn visible_len(&self) -> usize {
        if self.reset_confirmation.is_some() {
            2
        } else {
            self.items.len()
        }
    }

    fn build_rows(&self) -> Vec<GenericDisplayRow> {
        if let Some(state) = self.reset_confirmation.as_ref() {
            return ["Reset all memories", "Go back"]
                .into_iter()
                .enumerate()
                .map(|(idx, name)| GenericDisplayRow {
                    name: if state.selected_idx == Some(idx) {
                        format!("› {name}")
                    } else {
                        format!("  {name}")
                    },
                    description: Some(match idx {
                        0 => "Delete local memory files and rollout summaries.".to_string(),
                        1 => "Return to memory settings.".to_string(),
                        _ => unreachable!("reset confirmation only renders two rows"),
                    }),
                    ..Default::default()
                })
                .collect();
        }

        let selected_idx = self.state.selected_idx;
        self.items
            .iter()
            .enumerate()
            .map(|(idx, item)| {
                let prefix = if selected_idx == Some(idx) {
                    '›'
                } else {
                    ' '
                };
                let (name, description) = match item {
                    MemoriesMenuItem::Setting {
                        name,
                        description,
                        enabled,
                        ..
                    } => (
                        format!("{prefix} [{}] {name}", if *enabled { 'x' } else { ' ' }),
                        description,
                    ),
                    MemoriesMenuItem::Action {
                        name, description, ..
                    } => (format!("{prefix} {name}"), description),
                };
                GenericDisplayRow {
                    name,
                    description: Some((*description).to_string()),
                    ..Default::default()
                }
            })
            .collect()
    }

    fn move_up(&mut self) {
        let len = self.visible_len();
        if len == 0 {
            return;
        }
        let state = self.active_state_mut();
        state.move_up_wrap(len);
        state.ensure_visible(len, MAX_POPUP_ROWS.min(len));
    }

    fn move_down(&mut self) {
        let len = self.visible_len();
        if len == 0 {
            return;
        }
        let state = self.active_state_mut();
        state.move_down_wrap(len);
        state.ensure_visible(len, MAX_POPUP_ROWS.min(len));
    }

    fn page_up(&mut self) {
        let len = self.visible_len();
        let visible = MAX_POPUP_ROWS.min(len);
        self.active_state_mut().page_up_clamped(len, visible);
    }

    fn page_down(&mut self) {
        let len = self.visible_len();
        let visible = MAX_POPUP_ROWS.min(len);
        self.active_state_mut().page_down_clamped(len, visible);
    }

    fn jump_top(&mut self) {
        let len = self.visible_len();
        let visible = MAX_POPUP_ROWS.min(len);
        self.active_state_mut().jump_top(len, visible);
    }

    fn jump_bottom(&mut self) {
        let len = self.visible_len();
        let visible = MAX_POPUP_ROWS.min(len);
        self.active_state_mut().jump_bottom(len, visible);
    }

    fn toggle_selected(&mut self) {
        if self.reset_confirmation.is_some() {
            return;
        }

        let Some(selected_idx) = self.state.selected_idx else {
            return;
        };

        if let Some(MemoriesMenuItem::Setting { enabled, .. }) = self.items.get_mut(selected_idx) {
            *enabled = !*enabled;
        }
    }

    fn rows_width(total_width: u16) -> u16 {
        total_width.saturating_sub(2)
    }

    fn current_setting(&self, setting: MemoriesSetting) -> bool {
        self.items
            .iter()
            .find_map(|item| match item {
                MemoriesMenuItem::Setting {
                    setting: item_setting,
                    enabled,
                    ..
                } if *item_setting == setting => Some(*enabled),
                _ => None,
            })
            .unwrap_or(false)
    }

    fn open_reset_confirmation(&mut self) {
        let mut state = ScrollState::new();
        state.selected_idx = Some(0);
        self.reset_confirmation = Some(state);
    }

    fn close_reset_confirmation(&mut self) {
        self.reset_confirmation = None;
        self.state.selected_idx = self.items.len().checked_sub(1);
    }

    fn footer_hint(&self) -> Line<'static> {
        if self.reset_confirmation.is_some() {
            standard_popup_hint_line()
        } else {
            memories_settings_hint_line()
        }
    }
}

impl BottomPaneView for MemoriesSettingsView {
    fn handle_key_event(&mut self, key_event: KeyEvent) {
        match key_event {
            _ if self.keymap.move_up.is_pressed(key_event) => self.move_up(),
            _ if self.keymap.move_down.is_pressed(key_event) => self.move_down(),
            _ if self.keymap.page_up.is_pressed(key_event) => self.page_up(),
            _ if self.keymap.page_down.is_pressed(key_event) => self.page_down(),
            _ if self.keymap.jump_top.is_pressed(key_event) => self.jump_top(),
            _ if self.keymap.jump_bottom.is_pressed(key_event) => self.jump_bottom(),
            KeyEvent {
                code: KeyCode::Char(' '),
                modifiers: KeyModifiers::NONE,
                ..
            } => self.toggle_selected(),
            _ if self.keymap.accept.is_pressed(key_event) => self.save(),
            _ if self.keymap.cancel.is_pressed(key_event) => self.cancel(),
            _ => {}
        }
    }

    fn is_complete(&self) -> bool {
        self.complete
    }

    fn on_ctrl_c(&mut self) -> CancellationEvent {
        self.cancel();
        CancellationEvent::Handled
    }
}

impl MemoriesSettingsView {
    fn save(&mut self) {
        if let Some(state) = self.reset_confirmation.as_ref() {
            match state.selected_idx {
                Some(0) => {
                    self.app_event_tx.send(AppEvent::ResetMemories);
                    self.complete = true;
                }
                Some(1) | None => self.close_reset_confirmation(),
                Some(other) => unreachable!("unexpected reset confirmation row: {other}"),
            }
            return;
        }

        match self.state.selected_idx.and_then(|idx| self.items.get(idx)) {
            Some(MemoriesMenuItem::Action {
                action: MemoriesAction::Reset,
                ..
            }) => self.open_reset_confirmation(),
            _ => {
                self.app_event_tx.send(AppEvent::UpdateMemorySettings {
                    use_memories: self.current_setting(MemoriesSetting::Use),
                    generate_memories: self.current_setting(MemoriesSetting::Generate),
                });
                self.complete = true;
            }
        }
    }

    fn cancel(&mut self) {
        if self.reset_confirmation.is_some() {
            self.close_reset_confirmation();
        } else {
            self.complete = true;
        }
    }
}

impl Renderable for MemoriesSettingsView {
    fn render(&self, area: Rect, buf: &mut Buffer) {
        if area.height == 0 || area.width == 0 {
            return;
        }

        let [content_area, footer_area] =
            Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).areas(area);

        Block::default()
            .style(user_message_style())
            .render(content_area, buf);

        let header = if self.reset_confirmation.is_some() {
            self.reset_confirmation_header()
        } else {
            self.settings_header()
        };
        let header_height = header.desired_height(content_area.width.saturating_sub(4));
        let rows = self.build_rows();
        let rows_width = Self::rows_width(content_area.width);
        let rows_height = measure_rows_height(
            &rows,
            self.active_state(),
            MAX_POPUP_ROWS,
            rows_width.saturating_add(1),
        );
        let [header_area, _, list_area, _, docs_area] = Layout::vertical([
            Constraint::Max(header_height),
            Constraint::Max(1),
            Constraint::Length(rows_height),
            Constraint::Max(1),
            Constraint::Length(1),
        ])
        .areas(content_area.inset(Insets::vh(/*v*/ 1, /*h*/ 2)));

        header.render(header_area, buf);

        if list_area.height > 0 {
            let render_area = Rect {
                x: list_area.x.saturating_sub(2),
                y: list_area.y,
                width: rows_width.max(1),
                height: list_area.height,
            };
            render_rows(
                render_area,
                buf,
                &rows,
                self.active_state(),
                MAX_POPUP_ROWS,
                "  No memory settings available",
            );
        }
        if self.reset_confirmation.is_none() {
            self.docs_link.clone().render(docs_area, buf);
            crate::terminal_hyperlinks::mark_url_hyperlink(buf, docs_area, MEMORIES_DOC_URL);
        }

        let hint_area = Rect {
            x: footer_area.x + 2,
            y: footer_area.y,
            width: footer_area.width.saturating_sub(2),
            height: footer_area.height,
        };
        self.footer_hint().render(hint_area, buf);
    }

    fn desired_height(&self, width: u16) -> u16 {
        let header = if self.reset_confirmation.is_some() {
            self.reset_confirmation_header()
        } else {
            self.settings_header()
        };
        let rows = self.build_rows();
        let rows_width = Self::rows_width(width);
        let rows_height = measure_rows_height(
            &rows,
            self.active_state(),
            MAX_POPUP_ROWS,
            rows_width.saturating_add(1),
        );

        let docs_height = if self.reset_confirmation.is_some() {
            0
        } else {
            1
        };
        let mut height = header.desired_height(width.saturating_sub(4));
        height = height.saturating_add(rows_height + 4 + docs_height);
        height.saturating_add(1)
    }
}

fn memories_settings_hint_line() -> Line<'static> {
    Line::from(vec![
        "Press ".into(),
        key_hint::plain(KeyCode::Char(' ')).into(),
        " to toggle; ".into(),
        key_hint::plain(KeyCode::Enter).into(),
        " to save or select".into(),
    ])
}
