#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![allow(clippy::type_complexity)] // Frame and other ratatui types use complex generics

//! Terminal UI for syntaxis management
//!
//! This application provides an interactive terminal interface for managing projects,
//! viewing status, and managing lifecycle phases using ratatui and crossterm.

mod api;
mod app;
mod config;
mod events;
mod plugins;
mod screens;
mod terminal;
mod theme;
mod types;
mod ui;
mod utils;
mod websocket;

// New TUI enhancement screens (binary-only)
mod delete_confirm;
mod edit_task;
mod help;
mod theme_selector;

use anyhow::Result;
use app::{App, Screen};
use config::TuiConfig;
use crossterm::event::KeyCode;
use events::{AppEvent, EventHandler, KeyBindings};
use std::time::Duration;
use terminal::Terminal;
use tui_textarea::TextArea;

/// Main application entry point
#[tokio::main]
async fn main() -> Result<()> {
    // Initialize tracing
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()),
        )
        .init();

    // Load configuration
    let config = TuiConfig::load();
    config.log_config();

    // Configure API client with loaded config
    api::set_api_base(config.api.base_url.clone());

    // Initialize terminal
    let mut terminal = Terminal::new()?;
    let events = EventHandler::new(Duration::from_millis(config.app.refresh_interval_ms))?;

    // Create app
    let mut app = App::new();

    // Load configuration into app
    app.load_from_config(&config);

    app.load_projects().await?;

    // Setup WebSocket if enabled
    let (_ws_tx, mut ws_rx) = tokio::sync::mpsc::channel(100);

    if config.features.realtime_sync && !config.api.ws_url.is_empty() {
        let ws_url = config.api.ws_url.clone();
        let ws_tx_clone = _ws_tx.clone();

        tokio::spawn(async move {
            if let Err(e) = websocket::connect_with_retry(&ws_url, ws_tx_clone, 5).await {
                tracing::error!("WebSocket connection failed: {}", e);
            }
        });
    }

    // Main event loop - R-EVENT-LOOP pattern
    //
    // # Pattern: R-EVENT-LOOP
    // - Non-blocking event polling with 250ms timeout
    // - Dirty flag optimization: only render when state changes
    // - Clear separation: handle events → update state → render when dirty
    loop {
        // === RENDER (only if state changed) ===
        // Dirty flag pattern: only redraw when state has changed
        if app.dirty {
            terminal.draw(|f| {
                ui::render(f, &app);
            })?;
            app.mark_rendered(); // Clear dirty flag after render
        }

        // === WEBSOCKET EVENT HANDLING ===
        // Check for WebSocket events (non-blocking)
        if let Ok(event) = ws_rx.try_recv() {
            handle_websocket_event(&mut app, event).await;
        }

        // === EVENT HANDLING (non-blocking) ===
        // Poll with 250ms timeout to keep responsive while idle
        match events.next()? {
            AppEvent::Key(key) => {
                if KeyBindings::is_quit(key.code) {
                    app.quit();
                    break;
                }

                match app.screen {
                    Screen::ProjectsList => handle_projects_list_keys(&mut app, key.code).await,
                    Screen::ProjectDetail => handle_project_detail_keys(&mut app, key.code).await,
                    Screen::Security => handle_security_keys(&mut app, key.code),
                    Screen::CreateProject => handle_create_project_keys(&mut app, key.code),
                    Screen::Tasks => handle_tasks_keys(&mut app, key.code).await,
                    Screen::CreateTask => handle_create_task_keys(&mut app, key),
                    Screen::EditTask => handle_edit_task_keys(&mut app, key).await,
                    Screen::TasksFilter => handle_filter_keys(&mut app, key.code),
                    Screen::DeleteConfirm => handle_delete_confirm_keys(&mut app, key.code).await,
                    Screen::Help => handle_help_keys(&mut app, key.code),
                    Screen::ThemeSelector => handle_theme_selector_keys(&mut app, key.code),
                }
            }
            AppEvent::Tick => {
                // Periodic update (every 250ms)
                // Could be used for animations, async operations, etc.
            }
            _ => {}
        }

        if app.should_quit {
            break;
        }
    }

    Ok(())
}

/// Handle key events for projects list screen
///
/// # R-STATE-SEPARATION Pattern
/// Mutations go through app methods, not direct field assignment
async fn handle_projects_list_keys(app: &mut App, key: KeyCode) {
    if KeyBindings::is_down(key) {
        app.nav_down();
    } else if KeyBindings::is_up(key) {
        app.nav_up();
    } else if KeyBindings::is_enter(key) {
        app.select_project();
    } else if matches!(key, KeyCode::Char('c')) {
        app.create_project_screen(); // Uses method instead of direct assignment
    } else if matches!(key, KeyCode::Char('s')) {
        app.view_security(); // Uses method instead of direct assignment
    }
}

/// Handle key events for project detail screen
///
/// # R-STATE-SEPARATION Pattern
async fn handle_project_detail_keys(app: &mut App, key: KeyCode) {
    if matches!(key, KeyCode::Char('b') | KeyCode::Esc) {
        app.back_to_projects();
    } else if matches!(key, KeyCode::Char('t')) {
        app.view_tasks();
        // Load tasks when viewing the task screen
        if let Err(e) = app.load_tasks().await {
            app.error = Some(format!("Failed to load tasks: {}", e));
            app.dirty = true;
        }
    }
}

/// Handle key events for security screen
///
/// # R-STATE-SEPARATION Pattern
fn handle_security_keys(app: &mut App, key: KeyCode) {
    if matches!(key, KeyCode::Char('b') | KeyCode::Esc) {
        app.back_to_projects(); // Use method for consistency
    }
}

/// Handle key events for create project screen
///
/// # R-STATE-SEPARATION Pattern
fn handle_create_project_keys(app: &mut App, key: KeyCode) {
    if matches!(key, KeyCode::Esc) {
        app.back_to_projects(); // Use method for consistency
    } else if matches!(key, KeyCode::Char('b')) {
        app.back_to_projects();
    }
}

/// Handle key events for tasks screen
///
/// # R-STATE-SEPARATION Pattern
async fn handle_tasks_keys(app: &mut App, key: KeyCode) {
    // Help screen
    if matches!(key, KeyCode::Char('?') | KeyCode::F(1))
        || matches!(key, KeyCode::Char('h') if app.screen == Screen::Tasks)
    {
        app.show_help();
        return;
    }

    // Theme selector
    if matches!(key, KeyCode::Char('T')) {
        app.show_theme_selector();
        return;
    }

    // Toggle dark mode
    if matches!(key, KeyCode::Char('d') if matches!(key, KeyCode::Char('d'))) {
        // Check for Ctrl modifier would be better, but simplified for now
    }

    // Navigation
    if matches!(key, KeyCode::Char('b') | KeyCode::Esc) {
        app.back_to_project_detail();
    } else if KeyBindings::is_down(key) {
        app.task_nav_down();
    } else if KeyBindings::is_up(key) {
        app.task_nav_up();
    } else if matches!(key, KeyCode::Char(' ')) {
        app.toggle_task_completion();
    } else if matches!(key, KeyCode::Char('n')) {
        app.create_task_screen();
    } else if matches!(key, KeyCode::Char('e')) {
        app.start_edit_task();
    } else if matches!(key, KeyCode::Char('d') | KeyCode::Delete) {
        app.start_delete_task();
        // Handle inline mode confirmation
        if matches!(app.delete_confirmation_mode, app::DeleteMode::Inline)
            && app.delete_confirm_pending
        {
            if let Err(e) = app.confirm_delete_task().await {
                app.error = Some(format!("Failed to delete task: {}", e));
                app.dirty = true;
            }
        }
    } else if matches!(key, KeyCode::Char('/')) {
        app.show_filters();
    } else if matches!(key, KeyCode::Char('m')) {
        if let Err(e) = app.load_more_tasks().await {
            app.error = Some(format!("Failed to load more tasks: {}", e));
            app.dirty = true;
        }
    }
}

/// Handle key events for task creation screen
///
/// # R-STATE-SEPARATION Pattern
/// # R-TEXTAREA-BASIC Pattern
/// Handles text input and special keys for task creation
fn handle_create_task_keys(app: &mut App, key: crossterm::event::KeyEvent) {
    use crossterm::event::KeyModifiers;

    // Check for exit/cancel
    if matches!(key.code, KeyCode::Esc) {
        app.back_to_tasks();
        return;
    }

    // Check for submit (Ctrl+Enter)
    if matches!(key.code, KeyCode::Enter) && key.modifiers.contains(KeyModifiers::CONTROL) {
        // Validate task description
        match app.validate_task_description() {
            Ok(()) => {
                // In a full implementation, this would create the task via API
                app.task_form_error = None;
                // For now, just go back to tasks screen
                app.back_to_tasks();
                app.dirty = true;
            }
            Err(e) => {
                app.task_form_error = Some(e);
                app.dirty = true;
            }
        }
        return;
    }

    // Handle text input using shared helper
    handle_textarea_input(app, key);
}

/// Handle key events for task filter/sort screen
///
/// # R-STATE-SEPARATION Pattern
fn handle_filter_keys(app: &mut App, key: KeyCode) {
    match key {
        // Exit filter screen
        KeyCode::Esc | KeyCode::Char('b') => {
            app.back_to_task_view();
        }
        // Toggle completion filter (f for filter)
        KeyCode::Char('f') => {
            app.toggle_completion_filter();
        }
        // Clear all filters (c for clear)
        KeyCode::Char('c') => {
            app.clear_all_filters();
        }
        // Navigate sort options (↓ or j)
        KeyCode::Down | KeyCode::Char('j') => {
            app.next_sort_option();
        }
        // Navigate sort options (↑ or k)
        KeyCode::Up | KeyCode::Char('k') => {
            app.prev_sort_option();
        }
        // Apply selected sort
        KeyCode::Enter => {
            app.apply_sort_option();
            app.back_to_task_view();
        }
        // Text input for search (tab to switch to search mode)
        KeyCode::Char(c) => {
            app.add_search_char(c);
        }
        // Backspace for search
        KeyCode::Backspace => {
            app.backspace_search();
        }
        _ => {
            app.dirty = true;
        }
    }
}

/// Handle key events for edit task screen
///
/// # R-STATE-SEPARATION Pattern
/// # R-TEXTAREA-BASIC Pattern
async fn handle_edit_task_keys(app: &mut App, key: crossterm::event::KeyEvent) {
    use crossterm::event::KeyModifiers;

    // Check for exit/cancel
    if matches!(key.code, KeyCode::Esc) {
        app.cancel_edit_task();
        return;
    }

    // Check for submit (Ctrl+Enter)
    if matches!(key.code, KeyCode::Enter) && key.modifiers.contains(KeyModifiers::CONTROL) {
        if let Err(e) = app.submit_task_edit().await {
            tracing::error!("Failed to submit task edit: {}", e);
            // Error is already set in submit_task_edit
        }
        return;
    }

    // Handle text input (reuse logic from create_task)
    handle_textarea_input(app, key);
}

/// Handle key events for delete confirmation modal
///
/// # R-STATE-SEPARATION Pattern
async fn handle_delete_confirm_keys(app: &mut App, key: KeyCode) {
    match key {
        KeyCode::Char('y') | KeyCode::Enter => {
            if let Err(e) = app.confirm_delete_task().await {
                app.error = Some(format!("Failed to delete task: {}", e));
                app.dirty = true;
            }
        }
        KeyCode::Char('n') | KeyCode::Esc => {
            app.cancel_delete_task();
        }
        _ => {}
    }
}

/// Handle key events for help screen
///
/// # R-STATE-SEPARATION Pattern
fn handle_help_keys(app: &mut App, key: KeyCode) {
    match key {
        KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
            app.close_help();
        }
        KeyCode::Down | KeyCode::Char('j') => {
            app.help_scroll_down();
        }
        KeyCode::Up | KeyCode::Char('k') => {
            app.help_scroll_up();
        }
        _ => {}
    }
}

/// Handle key events for theme selector
///
/// # R-STATE-SEPARATION Pattern
fn handle_theme_selector_keys(app: &mut App, key: KeyCode) {
    match key {
        KeyCode::Esc | KeyCode::Char('q') => {
            app.close_theme_selector();
        }
        KeyCode::Down | KeyCode::Char('j') => {
            app.theme_nav_down();
        }
        KeyCode::Up | KeyCode::Char('k') => {
            app.theme_nav_up();
        }
        KeyCode::Enter => {
            if let Err(e) = app.apply_selected_theme() {
                app.error = Some(format!("Failed to apply theme: {}", e));
                app.dirty = true;
            }
        }
        _ => {}
    }
}

/// Handle WebSocket events
///
/// # R-STATE-SEPARATION Pattern
async fn handle_websocket_event(app: &mut App, event: websocket::ProjectEvent) {
    use websocket::ProjectEvent;

    match event {
        ProjectEvent::TaskCreated { project_id, .. }
        | ProjectEvent::TaskUpdated { project_id, .. }
        | ProjectEvent::TaskDeleted { project_id, .. }
        | ProjectEvent::TaskCompleted { project_id, .. } => {
            // Reload tasks if we're viewing the affected project
            if let Some(current_project) = &app.current_project {
                if current_project.id == project_id {
                    if let Err(e) = app.load_tasks().await {
                        tracing::error!("Failed to reload tasks after WebSocket event: {}", e);
                    }
                }
            }
        }
        ProjectEvent::ProjectUpdated { project_id: _ } => {
            // Reload projects list
            if let Err(e) = app.load_projects().await {
                tracing::error!("Failed to reload projects after WebSocket event: {}", e);
            }
        }
        ProjectEvent::Connected => {
            tracing::info!("WebSocket connected");
        }
        ProjectEvent::Ping => {
            // Ignore ping events
        }
    }
}

/// Helper function to handle textarea input
///
/// Shared between create_task and edit_task screens
fn handle_textarea_input(app: &mut App, key: crossterm::event::KeyEvent) {
    use crossterm::event::KeyModifiers;

    match key.code {
        // Regular character input
        KeyCode::Char(c) => {
            let mut lines = app.task_textarea.lines().to_vec();

            if matches!(c, '\t') {
                lines.iter_mut().for_each(|line| {
                    if !line.is_empty() {
                        *line = format!("{}  ", line);
                    }
                });
            } else if let Some(last) = lines.last_mut() {
                *last = format!("{}{}", last, c);
            } else {
                lines.push(c.to_string());
            }

            let content = lines.join("\n");
            app.task_textarea = TextArea::from(content.split('\n').collect::<Vec<_>>());
            app.task_form_error = None;
            app.dirty = true;
        }
        // Enter key - add new line
        KeyCode::Enter => {
            if !key.modifiers.contains(KeyModifiers::CONTROL) {
                let mut lines = app.task_textarea.lines().to_vec();
                lines.push(String::new());
                let content = lines.join("\n");
                app.task_textarea = TextArea::from(content.split('\n').collect::<Vec<_>>());
                app.task_form_error = None;
                app.dirty = true;
            }
        }
        // Backspace - remove last character
        KeyCode::Backspace => {
            let mut lines = app.task_textarea.lines().to_vec();
            if !lines.is_empty() {
                let should_pop = lines.len() > 1 && lines.last().is_some_and(|l| l.is_empty());
                if should_pop {
                    lines.pop();
                } else if let Some(last) = lines.last_mut() {
                    if !last.is_empty() {
                        last.pop();
                    }
                }
            }
            let content = lines.join("\n");
            app.task_textarea = TextArea::from(content.split('\n').collect::<Vec<_>>());
            app.task_form_error = None;
            app.dirty = true;
        }
        // Delete key - remove character at cursor
        KeyCode::Delete => {
            let mut lines = app.task_textarea.lines().to_vec();
            if let Some(last) = lines.last_mut() {
                if !last.is_empty() {
                    last.pop();
                }
            }
            let content = lines.join("\n");
            app.task_textarea = TextArea::from(content.split('\n').collect::<Vec<_>>());
            app.task_form_error = None;
            app.dirty = true;
        }
        _ => {
            app.dirty = true;
        }
    }
}
