use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Tabs},
    Frame, Terminal,
};
use sqlx::PgPool;
use std::time::Duration;

use crate::tui::error_waterfall;
use crate::tui::tabs::{
    AuthInspectorTab, CodeGenTab, DbTab, FeaturesTab, MigrationsTab, PerfTab, QueryProfilerTab,
    QueueTab, RoutesTab, SchemaVisualizerTab,
};

#[cfg(feature = "tui-studio")]
use rok_testing::studio::{StudioAction, StudioTab};

pub fn copy_to_clipboard(text: &str) {
    let result = if cfg!(target_os = "windows") {
        std::process::Command::new("clip")
            .stdin(std::process::Stdio::piped())
            .spawn()
            .and_then(|mut child| {
                use std::io::Write;
                child.stdin.take().unwrap().write_all(text.as_bytes())?;
                child.wait()?;
                Ok(())
            })
    } else if cfg!(target_os = "macos") {
        std::process::Command::new("pbcopy")
            .stdin(std::process::Stdio::piped())
            .spawn()
            .and_then(|mut child| {
                use std::io::Write;
                child.stdin.take().unwrap().write_all(text.as_bytes())?;
                child.wait()?;
                Ok(())
            })
    } else {
        std::process::Command::new("wl-copy")
            .stdin(std::process::Stdio::piped())
            .spawn()
            .and_then(|mut child| {
                use std::io::Write;
                child.stdin.take().unwrap().write_all(text.as_bytes())?;
                child.wait()?;
                Ok(())
            })
            .or_else(|_| {
                std::process::Command::new("xclip")
                    .args(["-selection", "clipboard"])
                    .stdin(std::process::Stdio::piped())
                    .spawn()
                    .and_then(|mut child| {
                        use std::io::Write;
                        child.stdin.take().unwrap().write_all(text.as_bytes())?;
                        child.wait()?;
                        Ok(())
                    })
            })
    };
    if result.is_err() {
        let _ = std::fs::write(std::env::temp_dir().join("rok-tui-clipboard.txt"), text);
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AppAction {
    None,
    Quit,
    Reload,
    LoadRows,
    Copy,
    ToggleCommandPalette,
    ProfileQuery,
    MigrateUp,
    MigrateDown,
    MigrateAll,
    AuthVerify,
    AuthLookupUser,
    AuthCheckBlacklist,
    #[cfg(feature = "tui-studio")]
    SendRequest,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandPaletteEntry {
    pub key: String,
    pub description: String,
    pub action: AppAction,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Tab {
    #[default]
    Db,
    Migrations,
    Features,
    Queue,
    Perf,
    QueryProfiler,
    Schema,
    AuthInspector,
    Routes,
    CodeGen,
    #[cfg(feature = "tui-studio")]
    Studio,
}

impl Tab {
    fn all() -> &'static [Tab] {
        #[cfg(not(feature = "tui-studio"))]
        {
            &[
                Tab::Db,
                Tab::Migrations,
                Tab::Features,
                Tab::Queue,
                Tab::Perf,
                Tab::QueryProfiler,
                Tab::Schema,
                Tab::AuthInspector,
                Tab::Routes,
                Tab::CodeGen,
            ]
        }
        #[cfg(feature = "tui-studio")]
        {
            &[
                Tab::Db,
                Tab::Migrations,
                Tab::Features,
                Tab::Queue,
                Tab::Perf,
                Tab::QueryProfiler,
                Tab::Schema,
                Tab::AuthInspector,
                Tab::Routes,
                Tab::CodeGen,
                Tab::Studio,
            ]
        }
    }

    fn index(self) -> usize {
        match self {
            Tab::Db => 0,
            Tab::Migrations => 1,
            Tab::Features => 2,
            Tab::Queue => 3,
            Tab::Perf => 4,
            Tab::QueryProfiler => 5,
            Tab::Schema => 6,
            Tab::AuthInspector => 7,
            Tab::Routes => 8,
            Tab::CodeGen => 9,
            #[cfg(feature = "tui-studio")]
            Tab::Studio => 10,
        }
    }

    fn label(self) -> &'static str {
        match self {
            Tab::Db => " DB Browser ",
            Tab::Migrations => " Migrations ",
            Tab::Features => " Features ",
            Tab::Queue => " Queue ",
            Tab::Perf => " Performance ",
            Tab::QueryProfiler => " Profiler ",
            Tab::Schema => " Schema ",
            Tab::AuthInspector => " Auth ",
            Tab::Routes => " Routes ",
            Tab::CodeGen => " Code Gen ",
            #[cfg(feature = "tui-studio")]
            Tab::Studio => " Studio ",
        }
    }

    fn next(self) -> Self {
        #[cfg(not(feature = "tui-studio"))]
        {
            match self {
                Tab::Db => Tab::Migrations,
                Tab::Migrations => Tab::Features,
                Tab::Features => Tab::Queue,
                Tab::Queue => Tab::Perf,
                Tab::Perf => Tab::QueryProfiler,
                Tab::QueryProfiler => Tab::Schema,
                Tab::Schema => Tab::AuthInspector,
                Tab::AuthInspector => Tab::Routes,
                Tab::Routes => Tab::CodeGen,
                Tab::CodeGen => Tab::Db,
            }
        }
        #[cfg(feature = "tui-studio")]
        {
            match self {
                Tab::Db => Tab::Migrations,
                Tab::Migrations => Tab::Features,
                Tab::Features => Tab::Queue,
                Tab::Queue => Tab::Perf,
                Tab::Perf => Tab::QueryProfiler,
                Tab::QueryProfiler => Tab::Schema,
                Tab::Schema => Tab::AuthInspector,
                Tab::AuthInspector => Tab::Routes,
                Tab::Routes => Tab::CodeGen,
                Tab::CodeGen => Tab::Studio,
                Tab::Studio => Tab::Db,
            }
        }
    }

    fn prev(self) -> Self {
        #[cfg(not(feature = "tui-studio"))]
        {
            match self {
                Tab::Db => Tab::CodeGen,
                Tab::Migrations => Tab::Db,
                Tab::Features => Tab::Migrations,
                Tab::Queue => Tab::Features,
                Tab::Perf => Tab::Queue,
                Tab::QueryProfiler => Tab::Perf,
                Tab::Schema => Tab::QueryProfiler,
                Tab::AuthInspector => Tab::Schema,
                Tab::Routes => Tab::AuthInspector,
                Tab::CodeGen => Tab::Routes,
            }
        }
        #[cfg(feature = "tui-studio")]
        {
            match self {
                Tab::Db => Tab::Studio,
                Tab::Migrations => Tab::Db,
                Tab::Features => Tab::Migrations,
                Tab::Queue => Tab::Features,
                Tab::Perf => Tab::Queue,
                Tab::QueryProfiler => Tab::Perf,
                Tab::Schema => Tab::QueryProfiler,
                Tab::AuthInspector => Tab::Schema,
                Tab::Routes => Tab::AuthInspector,
                Tab::CodeGen => Tab::Routes,
                Tab::Studio => Tab::CodeGen,
            }
        }
    }
}

pub struct App {
    pub pool: PgPool,
    pub current_tab: Tab,
    pub db_tab: DbTab,
    pub migrations_tab: MigrationsTab,
    pub features_tab: FeaturesTab,
    pub queue_tab: QueueTab,
    pub perf_tab: PerfTab,
    pub profiler_tab: QueryProfilerTab,
    pub schema_tab: SchemaVisualizerTab,
    pub auth_tab: AuthInspectorTab,
    pub routes_tab: RoutesTab,
    pub codegen_tab: CodeGenTab,
    #[cfg(feature = "tui-studio")]
    pub studio_tab: StudioTab,
    pub command_palette_open: bool,
    pub command_palette_state: ListState,
    pub command_palette_filter: String,
    pub status_message: String,
    pub status_timer: u8,
}

impl App {
    pub fn new(pool: PgPool) -> Self {
        let mut palette_state = ListState::default();
        palette_state.select(Some(0));
        Self {
            pool,
            current_tab: Tab::default(),
            db_tab: DbTab::default(),
            migrations_tab: MigrationsTab::default(),
            features_tab: FeaturesTab::default(),
            queue_tab: QueueTab::default(),
            perf_tab: PerfTab::default(),
            profiler_tab: QueryProfilerTab::default(),
            schema_tab: SchemaVisualizerTab::default(),
            auth_tab: AuthInspectorTab::default(),
            routes_tab: RoutesTab::default(),
            codegen_tab: CodeGenTab::default(),
            #[cfg(feature = "tui-studio")]
            studio_tab: StudioTab::default(),
            command_palette_open: false,
            command_palette_state: palette_state,
            command_palette_filter: String::new(),
            status_message: String::new(),
            status_timer: 0,
        }
    }

    pub async fn load_current(&mut self) {
        let pool = self.pool.clone();
        match self.current_tab {
            Tab::Db => self.db_tab.load(&pool).await,
            Tab::Migrations => self.migrations_tab.load(&pool).await,
            Tab::Features => self.features_tab.load(&pool).await,
            Tab::Queue => self.queue_tab.load(&pool).await,
            Tab::Perf => self.perf_tab.load(&pool).await,
            Tab::QueryProfiler => self.profiler_tab.load(&pool).await,
            Tab::Schema => self.schema_tab.load(&pool).await,
            Tab::AuthInspector => self.auth_tab.load(&pool).await,
            Tab::Routes => self.routes_tab.load(&pool).await,
            Tab::CodeGen => self.codegen_tab.load(&pool).await,
            #[cfg(feature = "tui-studio")]
            Tab::Studio => self.studio_tab.load().await,
        }
    }

    pub async fn load_rows(&mut self) {
        let pool = self.pool.clone();
        self.db_tab.load_rows(&pool).await;
    }

    pub fn render(&mut self, frame: &mut Frame) {
        let area = frame.area();

        if self.command_palette_open {
            self.render_command_palette(frame, area);
            return;
        }

        let error_count = error_waterfall::waterfall_count();
        let waterfall_h: u16 = if error_count > 0 {
            (error_count.min(5) as u16) + 1
        } else {
            0
        };

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

        self.render_tabs(frame, chunks[0]);
        self.render_body(frame, chunks[1]);
        if waterfall_h > 0 {
            error_waterfall::render_waterfall(frame, chunks[2]);
        }
        self.render_status_bar(frame, chunks[3]);
    }

    fn render_command_palette(&self, frame: &mut Frame, area: Rect) {
        let entries = self.get_palette_entries();
        let filtered: Vec<&CommandPaletteEntry> = if self.command_palette_filter.is_empty() {
            entries.iter().collect()
        } else {
            let f = self.command_palette_filter.to_lowercase();
            entries
                .iter()
                .filter(|e| {
                    e.key.to_lowercase().contains(&f) || e.description.to_lowercase().contains(&f)
                })
                .collect()
        };

        let items: Vec<ListItem> = filtered
            .iter()
            .map(|e| {
                ListItem::new(Line::from(vec![
                    Span::styled(format!(" {:15}", e.key), Style::default().fg(Color::Cyan)),
                    Span::raw(" "),
                    Span::styled(&e.description, Style::default().fg(Color::White)),
                ]))
            })
            .collect();

        let input = Paragraph::new(if self.command_palette_filter.is_empty() {
            " Type to filter commands..."
        } else {
            &self.command_palette_filter
        })
        .style(Style::default().fg(Color::Yellow))
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(" Command Palette (Ctrl+P) "),
        );

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

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

        frame.render_widget(input, chunks[0]);
        frame.render_stateful_widget(list, chunks[1], &mut self.command_palette_state.clone());
    }

    fn get_palette_entries(&self) -> Vec<CommandPaletteEntry> {
        vec![
            CommandPaletteEntry {
                key: "⌘Q".into(),
                description: "Quit".into(),
                action: AppAction::Quit,
            },
            CommandPaletteEntry {
                key: "F5".into(),
                description: "Refresh current tab".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "Ctrl+C".into(),
                description: "Copy selection to clipboard".into(),
                action: AppAction::Copy,
            },
            CommandPaletteEntry {
                key: "Tab".into(),
                description: fmt_tab_desc(self.current_tab, "Switch to next panel"),
                action: AppAction::None,
            },
            CommandPaletteEntry {
                key: "←→".into(),
                description: "Switch tabs".into(),
                action: AppAction::None,
            },
            CommandPaletteEntry {
                key: "F1".into(),
                description: "Go to DB Browser".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F2".into(),
                description: "Go to Migrations".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F3".into(),
                description: "Go to Features".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F4".into(),
                description: "Go to Queue".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F5".into(),
                description: "Go to Performance".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F6".into(),
                description: "Go to Profiler".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F7".into(),
                description: "Go to Schema".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F8".into(),
                description: "Go to Auth Inspector".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F9".into(),
                description: "Go to Routes".into(),
                action: AppAction::Reload,
            },
            CommandPaletteEntry {
                key: "F10".into(),
                description: "Go to Code Gen".into(),
                action: AppAction::Reload,
            },
        ]
    }

    fn render_tabs(&self, frame: &mut Frame, area: Rect) {
        let titles: Vec<Line> = Tab::all().iter().map(|t| Line::from(t.label())).collect();

        let tabs = Tabs::new(titles)
            .select(self.current_tab.index())
            .block(Block::default().borders(Borders::ALL).title(" rok-tui "))
            .highlight_style(
                Style::default()
                    .fg(Color::Cyan)
                    .add_modifier(Modifier::BOLD),
            )
            .style(Style::default().fg(Color::DarkGray));

        frame.render_widget(tabs, area);
    }

    fn render_body(&mut self, frame: &mut Frame, area: Rect) {
        match self.current_tab {
            Tab::Db => self.db_tab.render(frame, area),
            Tab::Migrations => self.migrations_tab.render(frame, area),
            Tab::Features => self.features_tab.render(frame, area),
            Tab::Queue => self.queue_tab.render(frame, area),
            Tab::Perf => self.perf_tab.render(frame, area),
            Tab::QueryProfiler => self.profiler_tab.render(frame, area),
            Tab::Schema => self.schema_tab.render(frame, area),
            Tab::AuthInspector => self.auth_tab.render(frame, area),
            Tab::Routes => self.routes_tab.render(frame, area),
            Tab::CodeGen => self.codegen_tab.render(frame, area),
            #[cfg(feature = "tui-studio")]
            Tab::Studio => self.studio_tab.render(frame, area),
        }
    }

    fn render_status_bar(&self, frame: &mut Frame, area: Rect) {
        let tabs_hint = "F1-F10";

        let message = if !self.status_message.is_empty() {
            format!("  {}", self.status_message)
        } else {
            String::new()
        };

        let help = Paragraph::new(Line::from(vec![
            Span::styled(format!(" {tabs_hint}"), Style::default().fg(Color::Cyan)),
            Span::raw("/"),
            Span::styled("←→", Style::default().fg(Color::Cyan)),
            Span::raw(" Tab switch  "),
            Span::styled("F5", Style::default().fg(Color::Cyan)),
            Span::raw(" Refresh  "),
            Span::styled("Ctrl+P", Style::default().fg(Color::Cyan)),
            Span::raw(" Palette  "),
            Span::styled("Ctrl+C", Style::default().fg(Color::Cyan)),
            Span::raw(" Copy  "),
            Span::styled("q", Style::default().fg(Color::Red)),
            Span::raw(" Quit"),
            Span::styled(message, Style::default().fg(Color::Green)),
        ]))
        .block(Block::default());
        frame.render_widget(help, area);
    }

    pub fn handle_global_key(&mut self, key: crossterm::event::KeyEvent) -> AppAction {
        if self.command_palette_open {
            match key.code {
                KeyCode::Esc => {
                    self.command_palette_open = false;
                    self.command_palette_filter.clear();
                    AppAction::None
                }
                KeyCode::Char(c) if !key.modifiers.contains(KeyModifiers::CONTROL) => {
                    self.command_palette_filter.push(c);
                    AppAction::None
                }
                KeyCode::Backspace => {
                    self.command_palette_filter.pop();
                    AppAction::None
                }
                KeyCode::Up => {
                    let i = self
                        .command_palette_state
                        .selected()
                        .unwrap_or(0)
                        .saturating_sub(1);
                    self.command_palette_state.select(Some(i));
                    AppAction::None
                }
                KeyCode::Down => {
                    let i = self.command_palette_state.selected().unwrap_or(0) + 1;
                    self.command_palette_state.select(Some(i));
                    AppAction::None
                }
                KeyCode::Enter => {
                    self.command_palette_open = false;
                    self.command_palette_filter.clear();
                    let entries = self.get_palette_entries();
                    let filtered: Vec<&CommandPaletteEntry> =
                        if self.command_palette_filter.is_empty() {
                            entries.iter().collect()
                        } else {
                            let f = self.command_palette_filter.to_lowercase();
                            entries
                                .iter()
                                .filter(|e| {
                                    e.key.to_lowercase().contains(&f)
                                        || e.description.to_lowercase().contains(&f)
                                })
                                .collect()
                        };
                    if let Some(selected) = self.command_palette_state.selected() {
                        if selected < filtered.len() {
                            return filtered[selected].action;
                        }
                    }
                    AppAction::None
                }
                _ => AppAction::None,
            }
        } else {
            match key.code {
                KeyCode::Char('q') | KeyCode::Char('Q')
                    if key.modifiers.contains(KeyModifiers::CONTROL)
                        || self.current_tab != Tab::Db =>
                {
                    AppAction::Quit
                }
                KeyCode::Char('p') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    AppAction::ToggleCommandPalette
                }
                KeyCode::Left | KeyCode::BackTab => {
                    self.current_tab = self.current_tab.prev();
                    AppAction::Reload
                }
                KeyCode::Right => {
                    self.current_tab = self.current_tab.next();
                    AppAction::Reload
                }
                KeyCode::F(1) => {
                    self.current_tab = Tab::Db;
                    AppAction::Reload
                }
                KeyCode::F(2) => {
                    self.current_tab = Tab::Migrations;
                    AppAction::Reload
                }
                KeyCode::F(3) => {
                    self.current_tab = Tab::Features;
                    AppAction::Reload
                }
                KeyCode::F(4) => {
                    self.current_tab = Tab::Queue;
                    AppAction::Reload
                }
                KeyCode::F(5) => {
                    self.current_tab = Tab::Perf;
                    AppAction::Reload
                }
                KeyCode::F(6) => {
                    self.current_tab = Tab::QueryProfiler;
                    AppAction::Reload
                }
                KeyCode::F(7) => {
                    self.current_tab = Tab::Schema;
                    AppAction::Reload
                }
                KeyCode::F(8) => {
                    self.current_tab = Tab::AuthInspector;
                    AppAction::Reload
                }
                KeyCode::F(9) => {
                    self.current_tab = Tab::Routes;
                    AppAction::Reload
                }
                KeyCode::F(10) => {
                    self.current_tab = Tab::CodeGen;
                    AppAction::Reload
                }
                _ => AppAction::None,
            }
        }
    }
}

fn fmt_tab_desc(current: Tab, action: &str) -> String {
    format!("{} — {}", action, current.label().trim())
}

pub async fn run_app<B: ratatui::backend::Backend>(
    terminal: &mut Terminal<B>,
    mut app: App,
) -> anyhow::Result<()> {
    app.load_current().await;

    loop {
        terminal.draw(|f| app.render(f))?;

        if !event::poll(Duration::from_millis(200))? {
            continue;
        }

        if let Event::Key(key) = event::read()? {
            let global_action = app.handle_global_key(key);

            let action = if global_action != AppAction::None {
                global_action
            } else {
                match app.current_tab {
                    Tab::Db => app.db_tab.handle_key(key),
                    Tab::Migrations => app.migrations_tab.handle_key(key),
                    Tab::Features => app.features_tab.handle_key(key),
                    Tab::Queue => app.queue_tab.handle_key(key),
                    Tab::Perf => app.perf_tab.handle_key(key),
                    Tab::QueryProfiler => app.profiler_tab.handle_key(key),
                    Tab::Schema => app.schema_tab.handle_key(key),
                    Tab::AuthInspector => app.auth_tab.handle_key(key),
                    Tab::Routes => app.routes_tab.handle_key(key),
                    Tab::CodeGen => app.codegen_tab.handle_key(key),
                    #[cfg(feature = "tui-studio")]
                    Tab::Studio => match app.studio_tab.handle_key(key) {
                        StudioAction::None => AppAction::None,
                        StudioAction::Reload => AppAction::Reload,
                        StudioAction::SendRequest => AppAction::SendRequest,
                    },
                }
            };

            match action {
                AppAction::Quit => break,
                AppAction::Reload => {
                    app.command_palette_open = false;
                    app.load_current().await;
                }
                AppAction::LoadRows => {
                    app.command_palette_open = false;
                    app.load_rows().await;
                }
                AppAction::Copy => {
                    let text = app.db_tab.selected_row_text();
                    if !text.is_empty() {
                        copy_to_clipboard(&text);
                        app.status_message = "✓ Copied to clipboard".into();
                        app.status_timer = 5;
                    }
                }
                AppAction::ToggleCommandPalette => {
                    app.command_palette_open = !app.command_palette_open;
                    if app.command_palette_open {
                        app.command_palette_filter.clear();
                        app.command_palette_state.select(Some(0));
                    }
                }
                AppAction::None => {}
                AppAction::AuthVerify => {
                    app.auth_tab.verify_signature(&app.pool).await;
                }
                AppAction::AuthLookupUser => {
                    app.auth_tab.lookup_user(&app.pool).await;
                }
                AppAction::AuthCheckBlacklist => {
                    app.auth_tab.check_blacklist(&app.pool).await;
                }
                AppAction::ProfileQuery => {
                    app.profiler_tab.explain_selected(&app.pool).await;
                    app.load_current().await;
                }
                AppAction::MigrateUp => {
                    app.migrations_tab.apply_up(&app.pool).await;
                    app.load_current().await;
                }
                AppAction::MigrateDown => {
                    app.migrations_tab.rollback_down(&app.pool).await;
                    app.load_current().await;
                }
                AppAction::MigrateAll => {
                    app.migrations_tab.apply_all(&app.pool).await;
                    app.load_current().await;
                }
                #[cfg(feature = "tui-studio")]
                AppAction::SendRequest => app.studio_tab.send_request_now().await,
            }
        }

        if app.status_timer > 0 {
            app.status_timer -= 1;
            if app.status_timer == 0 {
                app.status_message.clear();
            }
        }
    }

    Ok(())
}
