use std::io::{self, Stdout};
use std::path::PathBuf;
use std::time::Duration;

use anyhow::Result;
use crossterm::{
    event::{self, Event},
    execute,
    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
use ratatui::{
    Frame, Terminal,
    backend::CrosstermBackend,
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    widgets::{Block, Borders, Clear, Paragraph},
};

use crate::ui::app::{App, AppMode};
use crate::ui::category_panel::CategoryContent;
use crate::ui::formula_panel::FormulaContent;
use crate::ui::grid::GridWidget;
use crate::ui::help::HelpWidget;
use crate::ui::import_wizard_ui::ImportWizardWidget;
use crate::ui::panel::Panel;
use crate::ui::tile_bar::TileBar;
use crate::ui::view_panel::ViewContent;
use crate::ui::which_key::WhichKeyWidget;

struct TuiContext<'a> {
    terminal: Terminal<CrosstermBackend<&'a mut Stdout>>,
}

impl<'a> TuiContext<'a> {
    fn enter(out: &'a mut Stdout) -> Result<Self> {
        enable_raw_mode()?;
        execute!(out, EnterAlternateScreen)?;
        let backend = CrosstermBackend::new(out);
        let terminal = Terminal::new(backend)?;

        Ok(Self { terminal })
    }
}

impl<'a> Drop for TuiContext<'a> {
    fn drop(&mut self) {
        let _ = execute!(self.terminal.backend_mut(), LeaveAlternateScreen);
        let _ = disable_raw_mode();
    }
}

pub fn run_tui(
    workbook: crate::workbook::Workbook,
    file_path: Option<PathBuf>,
    import_value: Option<serde_json::Value>,
) -> Result<()> {
    let mut stdout = io::stdout();
    let mut tui_context = TuiContext::enter(&mut stdout)?;
    let mut app = App::new(workbook, file_path);

    if let Some(json) = import_value {
        app.start_import_wizard(json);
    } else if app.is_empty_model() {
        app.view_state.mode = AppMode::Help;
    }

    loop {
        tui_context.terminal.draw(|f| draw(f, &app))?;

        if event::poll(Duration::from_millis(100))? {
            match event::read()? {
                Event::Key(key) => {
                    app.handle_key(key)?;
                }
                Event::Resize(w, h) => {
                    app.term_width = w;
                    app.term_height = h;
                }
                _ => {}
            }
        }

        app.autosave_if_needed();

        if matches!(app.view_state.mode, AppMode::Quit) {
            break;
        }
    }

    Ok(())
}

// ── Drawing ──────────────────────────────────────────────────────────────────

fn fill_line(left: String, right: &str, width: u16) -> String {
    let pad = " ".repeat((width as usize).saturating_sub(left.len() + right.len()));
    format!("{left}{pad}{right}")
}

fn centered_popup(area: Rect, width: u16, height: u16) -> Rect {
    let w = width.min(area.width);
    let h = height.min(area.height);
    let x = area.x + area.width.saturating_sub(w) / 2;
    let y = area.y + area.height.saturating_sub(h) / 2;
    Rect::new(x, y, w, h)
}

fn draw_popup_frame(f: &mut Frame, popup: Rect, title: &str, border_color: Color) -> Rect {
    f.render_widget(Clear, popup);
    let block = Block::default()
        .borders(Borders::ALL)
        .border_style(Style::default().fg(border_color))
        .title(title);
    let inner = block.inner(popup);
    f.render_widget(block, popup);
    inner
}

fn mode_name(mode: &AppMode) -> &'static str {
    match mode {
        AppMode::Normal => "NORMAL",
        AppMode::Editing { .. } => "INSERT",
        AppMode::FormulaEdit { .. } => "FORMULA",
        AppMode::FormulaPanel => "FORMULAS",
        AppMode::CategoryPanel => "CATEGORIES",
        AppMode::CategoryAdd { .. } => "NEW CATEGORY",
        AppMode::ItemAdd { .. } => "ADD ITEMS",
        AppMode::ViewPanel => "VIEWS",
        AppMode::TileSelect => "TILES",
        AppMode::ImportWizard => "IMPORT",
        AppMode::ExportPrompt { .. } => "EXPORT",
        AppMode::CommandMode { .. } => "COMMAND",
        AppMode::Help => "HELP",
        AppMode::Quit => "QUIT",
        AppMode::RecordsNormal => "RECORDS",
        AppMode::RecordsEditing { .. } => "RECORDS INSERT",
    }
}

fn mode_style(mode: &AppMode) -> Style {
    match mode {
        AppMode::Editing { .. } | AppMode::RecordsEditing { .. } => {
            Style::default().fg(Color::Black).bg(Color::Green)
        }
        AppMode::CommandMode { .. } => Style::default().fg(Color::Black).bg(Color::Yellow),
        AppMode::TileSelect => Style::default().fg(Color::Black).bg(Color::Magenta),
        _ => Style::default().fg(Color::Black).bg(Color::DarkGray),
    }
}

fn draw(f: &mut Frame, app: &App) {
    let size = f.area();

    let main_chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(1), // title bar
            Constraint::Min(0),    // content
            Constraint::Length(1), // tile bar
            Constraint::Length(1), // status / command bar
        ])
        .split(size);

    draw_title(f, main_chunks[0], app);
    draw_content(f, main_chunks[1], app);
    draw_tile_bar(f, main_chunks[2], app);
    draw_bottom_bar(f, main_chunks[3], app);

    // Overlays (rendered last so they appear on top)
    if matches!(app.view_state.mode, AppMode::Help) {
        f.render_widget(HelpWidget::new(app.view_state.help_page), size);
    }
    if matches!(app.view_state.mode, AppMode::ImportWizard)
        && let Some(wizard) = &app.view_state.wizard
    {
        f.render_widget(ImportWizardWidget::new(wizard), size);
    }
    // ExportPrompt now uses the minibuffer at the bottom bar.
    if app.is_empty_model() && matches!(app.view_state.mode, AppMode::Normal | AppMode::CommandMode { .. }) {
        draw_welcome(f, main_chunks[1]);
    }

    // Which-key popup: show available completions after a prefix key
    if let Some(ref km) = app.view_state.transient_keymap {
        let hints = km.binding_hints();
        f.render_widget(WhichKeyWidget::new(&hints), size);
    }
}

fn draw_title(f: &mut Frame, area: Rect, app: &App) {
    let dirty = if app.model_state.dirty { " [+]" } else { "" };
    let file = app
        .model_state
        .file_path
        .as_ref()
        .and_then(|p| p.file_name())
        .and_then(|n| n.to_str())
        .map(|n| format!(" ({n})"))
        .unwrap_or_default();
    let title = format!(
        "  improvise  ·  {}{}{} ",
        app.model_state.workbook.model.name, file, dirty
    );
    let right = " ?:help  :q quit ";
    let line = fill_line(title, right, area.width);
    f.render_widget(
        Paragraph::new(line).style(
            Style::default()
                .fg(Color::Black)
                .bg(Color::Blue)
                .add_modifier(Modifier::BOLD),
        ),
        area,
    );
}

fn draw_content(f: &mut Frame, area: Rect, app: &App) {
    let side_open = app.view_state.formula_panel_open || app.view_state.category_panel_open || app.view_state.view_panel_open;

    let grid_area;
    if side_open {
        let side_w = 32u16;
        let chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Min(40), Constraint::Length(side_w)])
            .split(area);

        grid_area = chunks[0];

        let side = chunks[1];
        let panel_count = [
            app.view_state.formula_panel_open,
            app.view_state.category_panel_open,
            app.view_state.view_panel_open,
        ]
        .iter()
        .filter(|&&b| b)
        .count() as u16;
        let ph = side.height / panel_count.max(1);
        let mut y = side.y;

        if app.view_state.formula_panel_open {
            let a = Rect::new(side.x, y, side.width, ph);
            let content = FormulaContent::new(&app.model_state.workbook.model, &app.view_state.mode);
            f.render_widget(Panel::new(content, &app.view_state.mode, app.view_state.formula_cursor), a);
            y += ph;
        }
        if app.view_state.category_panel_open {
            let a = Rect::new(side.x, y, side.width, ph);
            let content = CategoryContent::new(
                &app.model_state.workbook.model,
                app.model_state.workbook.active_view(),
                &app.view_state.expanded_cats,
            );
            f.render_widget(Panel::new(content, &app.view_state.mode, app.view_state.cat_panel_cursor), a);
            y += ph;
        }
        if app.view_state.view_panel_open {
            let a = Rect::new(side.x, y, side.width, ph);
            let content = ViewContent::new(&app.model_state.workbook);
            f.render_widget(Panel::new(content, &app.view_state.mode, app.view_state.view_panel_cursor), a);
        }
    } else {
        grid_area = area;
    }

    f.render_widget(
        GridWidget::new(
            &app.model_state.workbook.model,
            app.model_state.workbook.active_view(),
            &app.model_state.workbook.active_view,
            &app.layout,
            &app.view_state.mode,
            &app.view_state.search_query,
            &app.view_state.buffers,
            app.view_state.drill_state.as_ref(),
        ),
        grid_area,
    );
}

fn draw_tile_bar(f: &mut Frame, area: Rect, app: &App) {
    f.render_widget(
        TileBar::new(
            &app.model_state.workbook.model,
            app.model_state.workbook.active_view(),
            &app.view_state.mode,
            app.view_state.tile_cat_idx,
        ),
        area,
    );
}

fn draw_bottom_bar(f: &mut Frame, area: Rect, app: &App) {
    if let Some(mb) = app.view_state.mode.minibuffer() {
        let buf = app
            .view_state
            .buffers
            .get(mb.buffer_key)
            .map(|s| s.as_str())
            .unwrap_or("");
        let text = format!("{}{}▌", mb.prompt, buf);
        f.render_widget(
            Paragraph::new(text).style(
                Style::default()
                    .fg(mb.color)
                    .bg(Color::Indexed(235))
                    .add_modifier(Modifier::BOLD),
            ),
            area,
        );
    } else {
        draw_status(f, area, app);
    }
}

fn draw_status(f: &mut Frame, area: Rect, app: &App) {
    let search_part = if app.view_state.search_mode {
        format!("  /{}▌", app.view_state.search_query)
    } else {
        String::new()
    };

    let msg = if !app.view_state.status_msg.is_empty() {
        app.view_state.status_msg.as_str()
    } else {
        app.hint_text()
    };

    let yank_indicator = if app.view_state.yanked.is_some() { " [yank]" } else { "" };
    let view_badge = format!("  {}{}  ", app.model_state.workbook.active_view, yank_indicator);

    let left = format!("  {}{search_part}  {msg}", mode_name(&app.view_state.mode));
    let line = fill_line(left, &view_badge, area.width);

    f.render_widget(Paragraph::new(line).style(mode_style(&app.view_state.mode)), area);
}

fn draw_welcome(f: &mut Frame, area: Rect) {
    let popup = centered_popup(area, 58, 20);
    let inner = draw_popup_frame(f, popup, "  Welcome to improvise  ", Color::Blue);

    let lines: &[(&str, Style)] = &[
        (
            "Multi-dimensional data modeling — in your terminal.",
            Style::default().fg(Color::White),
        ),
        ("", Style::default()),
        (
            "Getting started",
            Style::default()
                .fg(Color::Blue)
                .add_modifier(Modifier::BOLD),
        ),
        ("", Style::default()),
        (
            ":import <file>         Import JSON or CSV file",
            Style::default().fg(Color::Cyan),
        ),
        (
            ":add-cat <name>        Add a category (dimension)",
            Style::default().fg(Color::Cyan),
        ),
        (
            ":add-item <cat> <name> Add an item to a category",
            Style::default().fg(Color::Cyan),
        ),
        (
            ":formula <cat> <expr>  Add a formula, e.g.:",
            Style::default().fg(Color::Cyan),
        ),
        (
            "  Profit = Revenue - Cost",
            Style::default().fg(Color::Green),
        ),
        (
            ":w <file.improv>       Save your model",
            Style::default().fg(Color::Cyan),
        ),
        ("", Style::default()),
        (
            "Navigation",
            Style::default()
                .fg(Color::Blue)
                .add_modifier(Modifier::BOLD),
        ),
        ("", Style::default()),
        (
            "F C V                  Open panels (Formulas/Categories/Views)",
            Style::default(),
        ),
        (
            "T                      Tile-select: pivot rows ↔ cols ↔ page",
            Style::default(),
        ),
        ("i  Enter               Edit a cell", Style::default()),
        (
            "[ ]                    Cycle the page-axis filter",
            Style::default(),
        ),
        (
            "?  or  :help           Full key reference",
            Style::default(),
        ),
        (":q                     Quit", Style::default()),
    ];

    for (i, (text, style)) in lines.iter().enumerate() {
        if i >= inner.height as usize {
            break;
        }
        f.render_widget(
            Paragraph::new(*text).style(*style),
            Rect::new(
                inner.x + 1,
                inner.y + i as u16,
                inner.width.saturating_sub(2),
                1,
            ),
        );
    }
}
