use std::collections::HashMap;

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StudioAction {
    None,
    Reload,
    SendRequest,
}
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, Wrap},
    Frame,
};

use crate::{
    client::send_editor_request,
    registry::RouteInfo,
    tui::{
        collection::{Collection, SavedRequest},
        env::{ensure_studio_dirs, list_envs, load_env},
        history::{History, HistoryEntry},
        request::{EditField, RequestEditor},
        response::ResponseViewer,
    },
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Pane {
    #[default]
    Endpoints,
    Request,
    Response,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Overlay {
    None,
    EnvPicker,
    CollectionSave,
}

pub struct StudioTab {
    pub routes: Vec<RouteInfo>,
    pub filtered: Vec<usize>,
    pub list_state: ListState,
    pub filter: String,
    pub filter_mode: bool,
    pub request: RequestEditor,
    pub response: ResponseViewer,
    pub pane: Pane,
    pub overlay: Overlay,
    pub envs: Vec<String>,
    pub env_state: ListState,
    pub current_env: String,
    pub env_vars: HashMap<String, String>,
    pub history: History,
    pub status: String,
    pub base_url: String,
    pub pending_request: bool,
    pub save_name_buf: String,
}

impl Default for StudioTab {
    fn default() -> Self {
        let _ = ensure_studio_dirs();
        let envs = list_envs();
        let current_env = envs.first().cloned().unwrap_or_else(|| "dev".to_string());
        let env_vars = load_env(&current_env).unwrap_or_default();
        let base_url = env_vars
            .get("BASE_URL")
            .cloned()
            .unwrap_or_else(|| "http://localhost:3000".to_string());

        let mut s = Self {
            routes: Vec::new(),
            filtered: Vec::new(),
            list_state: ListState::default(),
            filter: String::new(),
            filter_mode: false,
            request: RequestEditor::default(),
            response: ResponseViewer::default(),
            pane: Pane::Endpoints,
            overlay: Overlay::None,
            envs,
            env_state: ListState::default(),
            current_env,
            env_vars,
            history: History::load(),
            status: "Ready — F5 to discover routes".into(),
            base_url,
            pending_request: false,
            save_name_buf: String::new(),
        };
        s.rebuild_filter();
        s
    }
}

impl StudioTab {
    pub async fn load(&mut self) {
        let url = format!("{}/__studio/routes", self.base_url.trim_end_matches('/'));
        match reqwest::get(&url).await {
            Ok(resp) if resp.status().is_success() => match resp.json::<Vec<RouteInfo>>().await {
                Ok(routes) => {
                    self.status = format!("{} routes discovered", routes.len());
                    self.routes = routes;
                }
                Err(e) => {
                    self.status = format!("Parse error: {e}");
                    self.try_load_fallback();
                }
            },
            _ => {
                self.try_load_fallback();
            }
        }
        self.rebuild_filter();
        if !self.filtered.is_empty() && self.list_state.selected().is_none() {
            self.list_state.select(Some(0));
        }
    }

    fn try_load_fallback(&mut self) {
        let path = ".rok-studio/routes.json";
        if let Ok(content) = std::fs::read_to_string(path) {
            if let Ok(routes) = serde_json::from_str::<Vec<RouteInfo>>(&content) {
                self.status = format!("{} routes (from cache)", routes.len());
                self.routes = routes;
                return;
            }
        }
        self.status = "Server unreachable — no route cache found".into();
    }

    fn rebuild_filter(&mut self) {
        let q = self.filter.to_lowercase();
        self.filtered = self
            .routes
            .iter()
            .enumerate()
            .filter(|(_, r)| {
                q.is_empty()
                    || r.path.to_lowercase().contains(&q)
                    || r.method.to_lowercase().contains(&q)
                    || r.handler.to_lowercase().contains(&q)
            })
            .map(|(i, _)| i)
            .collect();
    }

    fn load_selected_route(&mut self) {
        if let Some(sel) = self.list_state.selected() {
            if let Some(&idx) = self.filtered.get(sel) {
                if let Some(route) = self.routes.get(idx) {
                    self.request.load_route(route, &self.base_url);
                }
            }
        }
    }

    fn switch_env(&mut self, name: &str) {
        self.current_env = name.to_string();
        self.env_vars = load_env(name).unwrap_or_default();
        if let Some(url) = self.env_vars.get("BASE_URL") {
            self.base_url = url.clone();
        }
    }

    pub fn render(&mut self, frame: &mut Frame, area: Rect) {
        let main_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Min(5), Constraint::Length(1)])
            .split(area);

        let columns = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Length(28), Constraint::Min(10)])
            .split(main_chunks[0]);

        let right_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(55), Constraint::Percentage(45)])
            .split(columns[1]);

        self.render_endpoints(frame, columns[0]);
        self.render_request(frame, right_chunks[0]);
        self.render_response(frame, right_chunks[1]);
        self.render_hints(frame, main_chunks[1]);

        if self.overlay == Overlay::EnvPicker {
            self.render_env_picker(frame, area);
        }
    }

    fn render_endpoints(&mut self, frame: &mut Frame, area: Rect) {
        let focused = self.pane == Pane::Endpoints;
        let border_style = border_style(focused, Color::Cyan);

        let filter_line = if self.filter_mode {
            format!("Filter: {}█", self.filter)
        } else if self.filter.is_empty() {
            "Filter: /".to_string()
        } else {
            format!("Filter: {}", self.filter)
        };

        let items: Vec<ListItem> = self
            .filtered
            .iter()
            .map(|&idx| {
                let r = &self.routes[idx];
                let method_color = method_color(&r.method);
                ListItem::new(Line::from(vec![
                    Span::styled(
                        format!("{:<7}", r.method),
                        Style::default()
                            .fg(method_color)
                            .add_modifier(Modifier::BOLD),
                    ),
                    Span::raw(r.path.as_str()),
                ]))
            })
            .collect();

        let env_line = format!("ENV: {} ▼", self.current_env);
        let title = format!(" Studio [{env_line}] ");

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(1),
                Constraint::Min(0),
                Constraint::Length(1),
            ])
            .split(area);

        frame.render_widget(
            Paragraph::new(filter_line).style(Style::default().fg(Color::DarkGray)),
            chunks[0],
        );

        let list = List::new(items)
            .block(
                Block::default()
                    .title(title)
                    .borders(Borders::ALL)
                    .border_style(border_style),
            )
            .highlight_style(
                Style::default()
                    .bg(Color::DarkGray)
                    .add_modifier(Modifier::BOLD),
            )
            .highlight_symbol("▶ ");

        frame.render_stateful_widget(list, chunks[1], &mut self.list_state);

        frame.render_widget(
            Paragraph::new(format!("{} routes", self.filtered.len()))
                .style(Style::default().fg(Color::DarkGray)),
            chunks[2],
        );
    }

    fn render_request(&mut self, frame: &mut Frame, area: Rect) {
        let focused = self.pane == Pane::Request;
        let border_style = border_style(focused, Color::Yellow);

        let method_color = method_color(&self.request.method);
        let url_line = Line::from(vec![
            Span::styled(
                format!("  {}  ", self.request.method),
                Style::default()
                    .fg(method_color)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::raw(if self.request.url.is_empty() {
                "(no URL)"
            } else {
                &self.request.url
            }),
        ]);

        let header_rows: Vec<Row> = self
            .request
            .headers
            .iter()
            .enumerate()
            .map(|(i, (k, v))| {
                let editing_key = self.request.editing == EditField::HeaderKey(i);
                let editing_val = self.request.editing == EditField::HeaderVal(i);
                Row::new(vec![
                    Cell::from(if editing_key {
                        format!("{k}█")
                    } else {
                        k.clone()
                    })
                    .style(Style::default().fg(Color::Cyan)),
                    Cell::from(if editing_val {
                        format!("{v}█")
                    } else {
                        v.clone()
                    }),
                ])
            })
            .collect();

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(2),
                Constraint::Length(5),
                Constraint::Min(0),
            ])
            .split(area);

        frame.render_widget(
            Paragraph::new(url_line).block(
                Block::default()
                    .borders(Borders::TOP | Borders::LEFT | Borders::RIGHT)
                    .border_style(border_style)
                    .title(" Request "),
            ),
            chunks[0],
        );

        let header_table = Table::new(
            header_rows,
            [Constraint::Percentage(35), Constraint::Percentage(65)],
        )
        .block(
            Block::default()
                .title(" Headers ")
                .borders(Borders::LEFT | Borders::RIGHT)
                .border_style(border_style),
        )
        .header(Row::new(vec![
            Cell::from("Key").style(Style::default().add_modifier(Modifier::BOLD)),
            Cell::from("Value").style(Style::default().add_modifier(Modifier::BOLD)),
        ]));
        frame.render_widget(header_table, chunks[1]);

        let body_editing = self.request.editing == EditField::Body;
        let body_display = if body_editing {
            format!("{}█", self.request.body)
        } else {
            self.request.body.clone()
        };

        frame.render_widget(
            Paragraph::new(body_display)
                .wrap(Wrap { trim: false })
                .block(
                    Block::default()
                        .title(" Body ")
                        .borders(Borders::ALL)
                        .border_style(border_style),
                ),
            chunks[2],
        );
    }

    fn render_response(&mut self, frame: &mut Frame, area: Rect) {
        let focused = self.pane == Pane::Response;
        let border_style = border_style(focused, Color::Green);

        if self.response.status == 0 {
            frame.render_widget(
                Paragraph::new("No response yet — press Enter to send")
                    .style(Style::default().fg(Color::DarkGray))
                    .block(
                        Block::default()
                            .title(" Response ")
                            .borders(Borders::ALL)
                            .border_style(border_style),
                    ),
                area,
            );
            return;
        }

        let status_color = match self.response.status {
            200..=299 => Color::Green,
            300..=399 => Color::Cyan,
            400..=499 => Color::Yellow,
            _ => Color::Red,
        };

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(2), Constraint::Min(0)])
            .split(area);

        let status_line = Line::from(vec![
            Span::styled(
                format!(" {} ", self.response.status_label()),
                Style::default()
                    .fg(status_color)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                format!(" │ {}ms ", self.response.elapsed_ms),
                Style::default().fg(Color::DarkGray),
            ),
            Span::raw(self.response.content_type()),
        ]);
        frame.render_widget(
            Paragraph::new(status_line).block(
                Block::default()
                    .title(" Response ")
                    .borders(Borders::TOP | Borders::LEFT | Borders::RIGHT)
                    .border_style(border_style),
            ),
            chunks[0],
        );

        if self.response.show_headers {
            let header_text: String = self
                .response
                .headers
                .iter()
                .map(|(k, v)| format!("{k}: {v}\n"))
                .collect();
            frame.render_widget(
                Paragraph::new(header_text).block(
                    Block::default()
                        .borders(Borders::ALL)
                        .border_style(border_style),
                ),
                chunks[1],
            );
        } else {
            let lines: Vec<&str> = self.response.body.lines().collect();
            let visible: String = lines
                .iter()
                .skip(self.response.scroll)
                .cloned()
                .collect::<Vec<_>>()
                .join("\n");
            frame.render_widget(
                Paragraph::new(visible).wrap(Wrap { trim: false }).block(
                    Block::default()
                        .borders(Borders::BOTTOM | Borders::LEFT | Borders::RIGHT)
                        .border_style(border_style),
                ),
                chunks[1],
            );
        }
    }

    fn render_hints(&self, frame: &mut Frame, area: Rect) {
        let hints = Paragraph::new(Line::from(vec![
            Span::styled("Enter", Style::default().fg(Color::Cyan)),
            Span::raw(" Send  "),
            Span::styled("Tab", Style::default().fg(Color::Cyan)),
            Span::raw(" Pane  "),
            Span::styled("/", Style::default().fg(Color::Cyan)),
            Span::raw(" Filter  "),
            Span::styled("N", Style::default().fg(Color::Cyan)),
            Span::raw(" New  "),
            Span::styled("S", Style::default().fg(Color::Cyan)),
            Span::raw(" Save  "),
            Span::styled("H", Style::default().fg(Color::Cyan)),
            Span::raw(" Headers  "),
            Span::styled("E", Style::default().fg(Color::Cyan)),
            Span::raw(" Env  "),
            Span::styled("F5", Style::default().fg(Color::Cyan)),
            Span::raw(format!("  Discover  [{}]", self.status)),
        ]));
        frame.render_widget(hints, area);
    }

    fn render_env_picker(&mut self, frame: &mut Frame, area: Rect) {
        let popup_area = centered_rect(40, 40, area);
        let items: Vec<ListItem> = self
            .envs
            .iter()
            .map(|e| ListItem::new(e.as_str()))
            .collect();
        let list = List::new(items)
            .block(
                Block::default()
                    .title(" Select Environment ")
                    .borders(Borders::ALL)
                    .border_style(Style::default().fg(Color::Cyan)),
            )
            .highlight_style(
                Style::default()
                    .bg(Color::Cyan)
                    .fg(Color::Black)
                    .add_modifier(Modifier::BOLD),
            )
            .highlight_symbol("▶ ");
        frame.render_stateful_widget(list, popup_area, &mut self.env_state);
    }

    pub fn handle_key(&mut self, key: KeyEvent) -> StudioAction {
        if self.overlay == Overlay::EnvPicker {
            return self.handle_env_picker_key(key);
        }

        match key.code {
            KeyCode::Tab => {
                self.pane = match self.pane {
                    Pane::Endpoints => Pane::Request,
                    Pane::Request => Pane::Response,
                    Pane::Response => Pane::Endpoints,
                };
                StudioAction::None
            }
            KeyCode::F(5) | KeyCode::Char('r') | KeyCode::Char('R') if !self.is_editing() => {
                StudioAction::Reload
            }
            KeyCode::Enter => match self.pane {
                Pane::Endpoints => {
                    self.load_selected_route();
                    self.pane = Pane::Request;
                    StudioAction::None
                }
                Pane::Request | Pane::Response => StudioAction::SendRequest,
            },
            KeyCode::Char('/') if self.pane == Pane::Endpoints && !self.filter_mode => {
                self.filter_mode = true;
                StudioAction::None
            }
            KeyCode::Esc => {
                if self.filter_mode {
                    self.filter_mode = false;
                    self.filter.clear();
                    self.rebuild_filter();
                } else {
                    self.request.editing = EditField::None;
                }
                StudioAction::None
            }
            KeyCode::Char(c) if self.filter_mode => {
                self.filter.push(c);
                self.rebuild_filter();
                if let Some(sel) = self.list_state.selected() {
                    if sel >= self.filtered.len() {
                        self.list_state.select(if self.filtered.is_empty() {
                            None
                        } else {
                            Some(0)
                        });
                    }
                }
                StudioAction::None
            }
            KeyCode::Backspace if self.filter_mode => {
                self.filter.pop();
                self.rebuild_filter();
                StudioAction::None
            }
            KeyCode::Up => {
                match self.pane {
                    Pane::Endpoints => {
                        let i = self.list_state.selected().unwrap_or(0).saturating_sub(1);
                        self.list_state.select(Some(i));
                    }
                    Pane::Response => self.response.scroll_up(),
                    _ => {}
                }
                StudioAction::None
            }
            KeyCode::Down => {
                match self.pane {
                    Pane::Endpoints => {
                        let max = self.filtered.len().saturating_sub(1);
                        let i = (self.list_state.selected().unwrap_or(0) + 1).min(max);
                        self.list_state.select(Some(i));
                    }
                    Pane::Response => self.response.scroll_down(),
                    _ => {}
                }
                StudioAction::None
            }
            KeyCode::Char('n') | KeyCode::Char('N') if !self.is_editing() => {
                self.request.clear();
                self.response = ResponseViewer::default();
                self.pane = Pane::Request;
                StudioAction::None
            }
            KeyCode::Char('h') | KeyCode::Char('H') if !self.is_editing() => {
                self.response.show_headers = !self.response.show_headers;
                StudioAction::None
            }
            KeyCode::Char('e') | KeyCode::Char('E') if !self.is_editing() => {
                self.overlay = Overlay::EnvPicker;
                if let Some(idx) = self.envs.iter().position(|e| e == &self.current_env) {
                    self.env_state.select(Some(idx));
                }
                StudioAction::None
            }
            KeyCode::Char('s') | KeyCode::Char('S')
                if !self.is_editing() && key.modifiers.contains(KeyModifiers::CONTROL) =>
            {
                self.save_current_request();
                StudioAction::None
            }
            // Request editing
            KeyCode::Char(c) if self.pane == Pane::Request => {
                self.request.push_char(c);
                StudioAction::None
            }
            KeyCode::Backspace if self.pane == Pane::Request => {
                self.request.pop_char();
                StudioAction::None
            }
            _ => StudioAction::None,
        }
    }

    fn handle_env_picker_key(&mut self, key: KeyEvent) -> StudioAction {
        match key.code {
            KeyCode::Up => {
                let i = self.env_state.selected().unwrap_or(0).saturating_sub(1);
                self.env_state.select(Some(i));
            }
            KeyCode::Down => {
                let max = self.envs.len().saturating_sub(1);
                let i = (self.env_state.selected().unwrap_or(0) + 1).min(max);
                self.env_state.select(Some(i));
            }
            KeyCode::Enter => {
                if let Some(idx) = self.env_state.selected() {
                    if let Some(name) = self.envs.get(idx).cloned() {
                        self.switch_env(&name);
                    }
                }
                self.overlay = Overlay::None;
            }
            KeyCode::Esc => {
                self.overlay = Overlay::None;
            }
            _ => {}
        }
        StudioAction::None
    }

    fn is_editing(&self) -> bool {
        self.request.editing != EditField::None || self.filter_mode
    }

    fn save_current_request(&mut self) {
        let saved = SavedRequest {
            name: format!("{} {}", self.request.method, self.request.url),
            method: self.request.method.clone(),
            path: self.request.url.clone(),
            headers: self.request.headers.iter().cloned().collect(),
            body: self.request.body.clone(),
        };
        let mut coll = Collection::load("default").unwrap_or_else(|_| Collection {
            name: "default".to_string(),
            requests: Vec::new(),
        });
        coll.add_request(saved);
        let _ = coll.save();
        self.status = "Saved to default collection".into();
    }

    pub async fn send_request_now(&mut self) {
        self.status = "Sending...".into();
        match send_editor_request(&self.request, &self.env_vars).await {
            Ok((status, elapsed, headers, body)) => {
                self.status = format!("{status} — {elapsed}ms");
                self.history.push(HistoryEntry {
                    method: self.request.method.clone(),
                    url: self.request.url.clone(),
                    status,
                    elapsed_ms: elapsed,
                });
                self.response.fill(status, elapsed, headers, body);
                self.pane = Pane::Response;
            }
            Err(e) => {
                self.status = format!("Error: {e}");
            }
        }
    }
}

fn method_color(method: &str) -> Color {
    match method {
        "GET" => Color::Green,
        "POST" => Color::Yellow,
        "PUT" => Color::Blue,
        "PATCH" => Color::Cyan,
        "DELETE" => Color::Red,
        "HEAD" => Color::Magenta,
        _ => Color::White,
    }
}

fn border_style(focused: bool, color: Color) -> Style {
    if focused {
        Style::default().fg(color)
    } else {
        Style::default()
    }
}

fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage((100 - percent_y) / 2),
            Constraint::Percentage(percent_y),
            Constraint::Percentage((100 - percent_y) / 2),
        ])
        .split(area);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage((100 - percent_x) / 2),
            Constraint::Percentage(percent_x),
            Constraint::Percentage((100 - percent_x) / 2),
        ])
        .split(popup_layout[1])[1]
}
