use std::rc::Rc;

use super::root::layout;
use crate::model::headers::{AuthSelection, HeadersModel, HeadersTab};
use crate::view::history_tab::HistoryTabs;
use crate::widgets::editor::{view_single_selected, view_single_unselected};
use crate::widgets::kv::KV;
use edtui::{EditorMode, EditorState, EditorStatusLine};
use ratatui::{
    prelude::*,
    widgets::{Block, StatefulWidget, Tabs, Widget},
};
use tui_widget_list::{ListBuilder, ListState, ListView};
use wireman_theme::Theme;

pub struct HeadersPage<'a> {
    model: Rc<std::cell::RefCell<HeadersModel>>,
    pub history_tabs_area: Option<&'a mut Option<[Rect; 5]>>,
}

impl<'a> HeadersPage<'a> {
    pub fn new(model: std::rc::Rc<std::cell::RefCell<HeadersModel>>) -> Self {
        Self {
            model,
            history_tabs_area: None,
        }
    }

    pub fn with_history_tabs_area(mut self, area: &'a mut Option<[Rect; 5]>) -> Self {
        self.history_tabs_area = Some(area);
        self
    }

    pub fn footer_keys(model: &HeadersModel) -> Vec<(&'static str, &'static str)> {
        let mut keys = vec![("Tab", "Proceed"), ("j/k/h/l", "Navigate")];
        if model.mode() == EditorMode::Insert {
            keys.push(("Esc", "Normal"));
        } else {
            keys.push(("i", "Insert"));
            keys.push(("<C-e>", "Open in Editor"));
        }

        keys
    }
}

impl Widget for HeadersPage<'_> {
    fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer) {
        use ratatui::layout::Constraint::{Length, Min};
        let theme = wireman_theme::Theme::global();
        let sl = u16::from(!theme.hide_status);
        let [addr_title, addr_content, _, auth_title, auth_content, _, meta_title, meta_content, status] =
            layout(area, Direction::Vertical, &[1, 3, 1, 1, 4, 1, 1, 0, sl]);

        {
            let mut model_mut = self.model.borrow_mut();
            model_mut.addr_title_area = Some(addr_title);
            model_mut.addr_content_area = Some(addr_content);
            model_mut.auth_title_area = Some(auth_title);
            model_mut.auth_content_area = Some(auth_content);
            model_mut.meta_title_area = Some(meta_title);
            model_mut.meta_content_area = Some(meta_content);
        }

        let model = self.model.borrow();
        let is_auth_selected = model.tab == HeadersTab::Auth;

        // Address
        let style = if model.tab == HeadersTab::Addr {
            theme.title.focused
        } else {
            theme.title.unfocused
        };
        Block::default()
            .title(String::from(" Address "))
            .title_alignment(Alignment::Center)
            .title_style(style)
            .render(addr_title, buf);

        Address {
            state: model.addr.state.clone(),
            title: String::new(),
            selected: model.tab == HeadersTab::Addr,
        }
        .render(addr_content, buf);

        // Authentication
        let style = if model.tab == HeadersTab::Auth {
            theme.title.focused
        } else {
            theme.title.unfocused
        };
        Block::default()
            .title(String::from(" Authentication "))
            .title_alignment(Alignment::Center)
            .title_style(style)
            .render(auth_title, buf);

        let body = match model.auth.selected {
            AuthSelection::Bearer => Authentication {
                state: model.auth.bearer.state.clone(),
                title: String::new(),
                selected: is_auth_selected,
                selected_tag: 0,
            },
            AuthSelection::Basic => Authentication {
                state: model.auth.basic.state.clone(),
                title: String::new(),
                selected: is_auth_selected,
                selected_tag: 1,
            },
        };
        body.render(auth_content, buf);

        // Metadata
        let style = if model.tab == HeadersTab::Meta {
            theme.title.focused
        } else {
            theme.title.unfocused
        };
        Block::default()
            .title(String::from(" Headers "))
            .title_alignment(Alignment::Center)
            .title_style(style)
            .render(meta_title, buf);

        let headers = &model.meta.headers;
        let index = model.meta.selected;
        Metadata {
            content: headers
                .iter()
                .enumerate()
                .map(|(i, x)| KV {
                    key: x.0.state.clone(),
                    val: x.1.state.clone(),
                    key_selected: (model.tab == HeadersTab::Meta)
                        && index.is_some_and(|x| x.row == i && x.col == 0),
                    val_selected: (model.tab == HeadersTab::Meta)
                        && index.is_some_and(|x| x.row == i && x.col == 1),
                    show_border_title: (model.tab == HeadersTab::Meta)
                        && index.is_some_and(|x| x.row == i),
                })
                .collect(),
            selected_row: index.map(|x| x.row),
            focused: model.tab == HeadersTab::Meta,
        }
        .render(meta_content, buf);

        let [s, h] = Layout::horizontal([Min(0), Length(60)]).areas(status);

        let status_line = EditorStatusLine::default()
            .style_mode(theme.highlight.unfocused.reversed())
            .style_search(theme.base.unfocused)
            .style_line(theme.base.unfocused)
            .mode(model.mode().name());
        status_line.render(s, buf);

        let history_model = model.history.borrow().clone();
        let selected_method = model.selected_method.clone();
        drop(model);
        let mut history = HistoryTabs::new(history_model, selected_method, true);
        if let Some(areas_ref) = self.history_tabs_area {
            history = history.with_tab_areas(areas_ref);
        }
        history.render(h, buf);
    }
}

#[derive(Clone)]
pub struct Address {
    pub state: EditorState,
    pub title: String,
    pub selected: bool,
}

impl Widget for Address {
    fn render(mut self, area: Rect, buf: &mut Buffer) {
        if self.selected {
            view_single_selected(&mut self.state, &self.title).render(area, buf);
        } else {
            view_single_unselected(&mut self.state, &self.title).render(area, buf);
        }
    }
}

#[derive(Clone)]
pub struct Authentication {
    pub state: EditorState,
    pub title: String,
    pub selected: bool,
    pub selected_tag: usize,
}

impl Widget for Authentication {
    fn render(mut self, area: Rect, buf: &mut Buffer) {
        let theme = Theme::global();
        let [title, content] = layout(area, Direction::Vertical, &[1, 3]);

        let (highlight_style, style) = if self.selected {
            (theme.title.focused, theme.base.unfocused)
        } else {
            (theme.title.unfocused, theme.base.unfocused)
        };

        let mut info_text = "";
        if self.selected {
            if self.selected_tag == 0 {
                info_text = "Without \"Bearer\".";
            } else {
                info_text = "Base64 encoded username:password. Without \"Basic\".";
            };
        }
        if info_text.len() as u16 > area.width - 28 {
            info_text = "";
        }

        let [left, right] = layout(title, Direction::Horizontal, &[0, info_text.len() as u16]);

        if self.selected {
            Line::from(info_text)
                .style(theme.base.unfocused)
                .render(right, buf);
        }

        let titles = if self.selected {
            vec![" Bearer (H) ", " Basic (L) "]
        } else {
            vec![" Bearer ", " Basic "]
        };
        Tabs::new(titles)
            .style(style)
            .highlight_style(highlight_style)
            .select(self.selected_tag)
            .divider("")
            .render(left.inner(Margin::new(0, 0)), buf);

        if self.selected {
            view_single_selected(&mut self.state, self.title).render(content, buf);
        } else {
            view_single_unselected(&mut self.state, self.title).render(content, buf);
        }
    }
}

#[derive(Clone)]
struct Metadata {
    content: Vec<KV>,
    selected_row: Option<usize>,
    focused: bool,
}

impl Widget for Metadata {
    fn render(self, area: Rect, buf: &mut Buffer) {
        let theme = Theme::global();

        let list_height = self.content.len() as u16 * 3;
        let [main, text] = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(list_height),
                Constraint::Length(u16::from(self.focused) * 2),
            ])
            .areas(area);

        let mut state = ListState::default();
        state.selected = self.selected_row;
        let item_count = self.content.len();
        let list = ListView::new(
            ListBuilder::new(move |context| {
                let item = self.content[context.index].clone();
                (item, 3)
            }),
            item_count,
        );
        list.render(main, buf, &mut state);
        if self.focused {
            let [add, del] = Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(1), Constraint::Length(1)])
                .areas(text);
            Line::from(vec![
                Span::from("<C-a>: ").style(theme.title.unfocused),
                Span::from("Add header").style(theme.base.unfocused),
            ])
            .render(add, buf);
            Line::from(vec![
                Span::from("<C-d>: ").style(theme.title.unfocused),
                Span::from("Delete header").style(theme.base.unfocused),
            ])
            .render(del, buf);
        }
    }
}
