//! App state and main event loop.

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

use crate::tabs::{DbTab, FeaturesTab, MigrationsTab, QueueTab};

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AppAction {
    None,
    Quit,
    Reload,
    LoadRows,
    #[cfg(feature = "studio")]
    SendRequest,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Tab {
    #[default]
    Db,
    Migrations,
    Features,
    Queue,
    #[cfg(feature = "studio")]
    Studio,
}

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

    fn index(self) -> usize {
        match self {
            Tab::Db => 0,
            Tab::Migrations => 1,
            Tab::Features => 2,
            Tab::Queue => 3,
            #[cfg(feature = "studio")]
            Tab::Studio => 4,
        }
    }

    fn label(self) -> &'static str {
        match self {
            Tab::Db => " DB Browser ",
            Tab::Migrations => " Migrations ",
            Tab::Features => " Features ",
            Tab::Queue => " Queue ",
            #[cfg(feature = "studio")]
            Tab::Studio => " Studio ",
        }
    }

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

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

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,
    #[cfg(feature = "studio")]
    pub studio_tab: StudioTab,
}

impl App {
    pub fn new(pool: PgPool) -> Self {
        Self {
            pool,
            current_tab: Tab::default(),
            db_tab: DbTab::default(),
            migrations_tab: MigrationsTab::default(),
            features_tab: FeaturesTab::default(),
            queue_tab: QueueTab::default(),
            #[cfg(feature = "studio")]
            studio_tab: StudioTab::default(),
        }
    }

    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,
            #[cfg(feature = "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();
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(3),
                Constraint::Min(0),
                Constraint::Length(1),
            ])
            .split(area);

        self.render_tabs(frame, chunks[0]);
        self.render_body(frame, chunks[1]);
        self.render_status_bar(frame, chunks[2]);
    }

    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),
            #[cfg(feature = "studio")]
            Tab::Studio => self.studio_tab.render(frame, area),
        }
    }

    fn render_status_bar(&self, frame: &mut Frame, area: Rect) {
        let tabs_hint = if cfg!(feature = "studio") {
            "F1-F5"
        } else {
            "F1-F4"
        };
        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(" Switch tab  "),
            Span::styled("F5", Style::default().fg(Color::Cyan)),
            Span::raw("/"),
            Span::styled("r", Style::default().fg(Color::Cyan)),
            Span::raw(" Refresh  "),
            Span::styled("Tab", Style::default().fg(Color::Cyan)),
            Span::raw(" Focus panel  "),
            Span::styled("q", Style::default().fg(Color::Red)),
            Span::raw(" Quit"),
        ]))
        .block(Block::default());
        frame.render_widget(help, area);
    }

    pub fn handle_global_key(&mut self, key: crossterm::event::KeyEvent) -> AppAction {
        match key.code {
            KeyCode::Char('q') | KeyCode::Char('Q')
                if key.modifiers.contains(KeyModifiers::CONTROL) || self.current_tab != Tab::Db =>
            {
                AppAction::Quit
            }
            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
            }
            #[cfg(feature = "studio")]
            KeyCode::F(5) => {
                self.current_tab = Tab::Studio;
                AppAction::Reload
            }
            _ => AppAction::None,
        }
    }
}

pub async fn run_app<B: ratatui::backend::Backend>(
    terminal: &mut Terminal<B>,
    mut app: App,
) -> anyhow::Result<()> {
    // Initial load
    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()? {
            // Global keys first (tab switching, quit)
            let global_action = app.handle_global_key(key);

            let action = if global_action != AppAction::None {
                global_action
            } else {
                // Delegate to active tab
                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),
                    #[cfg(feature = "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.load_current().await,
                AppAction::LoadRows => app.load_rows().await,
                AppAction::None => {}
                #[cfg(feature = "studio")]
                AppAction::SendRequest => app.studio_tab.send_request_now().await,
            }
        }
    }

    Ok(())
}
