//! Main TUI application
//!
//! This module contains the main application struct and all its methods.
//! The App struct manages the TUI state, handles user input, and coordinates
//! domain checking operations.

use crate::cache::CacheManager;
use crate::checker::RdapClient;
use crate::config::Config;
use crate::error::{LazyDomainError, Result};
use crate::types::{AppState, CheckResult, Domain, TldPreset};
use crate::{export, ui};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, widgets::TableState, Terminal};
use std::io;
use std::sync::mpsc::{self, Receiver, Sender};
use tui_textarea::TextArea;

pub mod enums;

pub use enums::ViewTab;
pub(crate) use enums::CacheFocus;

/// Main TUI application
pub struct App<'a> {
    /// Application config
    _config: Config,
    /// Current application state
    state: AppState,
    /// Current view tab
    current_tab: ViewTab,
    /// Search input textarea with terminal-style editing
    search_input: TextArea<'a>,
    /// Check results
    results: Vec<CheckResult>,
    /// Results table state
    table_state: TableState,
    /// Cache table state (for selecting cached searches)
    cache_table_state: TableState,
    /// Cache result table state (for selecting results within a cached search)
    cache_result_table_state: TableState,
    /// Cache view focus state
    cache_focus: CacheFocus,
    /// Selected TLD preset
    tld_preset: TldPreset,
    /// RDAP client
    #[allow(dead_code)]
    rdap_client: RdapClient,
    /// Cache manager
    cache_manager: CacheManager,
    /// Whether to quit
    should_quit: bool,
    /// Channel for receiving domain check results
    result_receiver: Option<Receiver<CheckResult>>,
    /// Expected number of results (to know when search is complete)
    expected_results: usize,
    /// Animation frame counter for loading spinner
    spinner_frame: usize,
    /// Current search query (for caching)
    current_query: String,
}

impl App<'static> {
    /// Creates a new App instance
    pub fn new() -> Result<Self> {
        let config = Config::load()?;
        let rdap_client = RdapClient::new()?;
        let cache_manager = CacheManager::new()?;

        // Create a single-line text area for search input
        let mut search_input = TextArea::default();
        search_input.set_placeholder_text("Type domain name...");
        search_input.set_max_histories(0); // Disable undo/redo for single-line input

        Ok(App {
            _config: config,
            state: AppState::Idle,
            current_tab: ViewTab::Search,
            search_input,
            results: Vec::new(),
            table_state: TableState::default(),
            cache_table_state: TableState::default(),
            cache_result_table_state: TableState::default(),
            cache_focus: CacheFocus::Sidebar,
            tld_preset: TldPreset::Popular,
            rdap_client,
            cache_manager,
            should_quit: false,
            result_receiver: None,
            expected_results: 0,
            spinner_frame: 0,
            current_query: String::new(),
        })
    }

    /// Runs the TUI application
    pub fn run(mut self) -> Result<()> {
        // Setup terminal
        enable_raw_mode().map_err(|e| LazyDomainError::TuiError(e.to_string()))?;
        let mut stdout = io::stdout();
        execute!(stdout, EnterAlternateScreen, EnableMouseCapture)
            .map_err(|e| LazyDomainError::TuiError(e.to_string()))?;
        let backend = CrosstermBackend::new(stdout);
        let mut terminal = Terminal::new(backend)
            .map_err(|e| LazyDomainError::TuiError(e.to_string()))?;

        let result = self.run_event_loop(&mut terminal);

        // Restore terminal
        disable_raw_mode().map_err(|e| LazyDomainError::TuiError(e.to_string()))?;
        execute!(
            terminal.backend_mut(),
            LeaveAlternateScreen,
            DisableMouseCapture
        )
        .map_err(|e| LazyDomainError::TuiError(e.to_string()))?;
        terminal
            .show_cursor()
            .map_err(|e| LazyDomainError::TuiError(e.to_string()))?;

        result
    }

    /// Main event loop
    fn run_event_loop(&mut self, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> Result<()> {
        loop {
            // Update spinner animation when searching
            if self.state == AppState::Searching {
                self.spinner_frame = (self.spinner_frame + 1) % 8;
            }
            
            terminal
                .draw(|f| self.render(f))
                .map_err(|e| LazyDomainError::TuiError(e.to_string()))?;

            // Check for incoming domain check results
            if let Some(receiver) = &self.result_receiver {
                if let Ok(result) = receiver.try_recv() {
                    self.results.push(result);
                    // If we got a result and table isn't selected yet, select first item
                    if self.results.len() == 1 {
                        self.table_state.select(Some(0));
                    }
                    
                    // Check if all results received
                    if self.results.len() >= self.expected_results {
                        self.state = AppState::Results;
                        self.result_receiver = None; // Drop receiver
                        
                        // Cache the completed search results
                        if !self.current_query.is_empty() && !self.results.is_empty() {
                            let _ = self.cache_manager.add_search(&self.current_query, self.results.clone());
                        }
                    }
                }
            }

            if event::poll(std::time::Duration::from_millis(100))
                .map_err(|e| LazyDomainError::TuiError(e.to_string()))?
            {
                if let Event::Key(key) = event::read()
                    .map_err(|e| LazyDomainError::TuiError(e.to_string()))?
                {
                    self.handle_key_event(key)?;
                }
            }

            if self.should_quit {
                break;
            }
        }

        Ok(())
    }

    /// Renders the UI
    fn render(&mut self, frame: &mut ratatui::Frame) {
        match self.current_tab {
            ViewTab::Search => {
                let chunks = ui::create_layout(frame.area());

                ui::render_tab_bar(frame, chunks[0], &self.current_tab);
                ui::render_search_panel(frame, chunks[1], &self.search_input, &self.state, self.spinner_frame);
                ui::render_results_table(frame, chunks[2], &self.results, &mut self.table_state);
                
                let selected_result = self.table_state.selected().and_then(|i| self.results.get(i));
                ui::render_details_panel(frame, chunks[3], selected_result);
                ui::render_status_bar(frame, chunks[4], self.results.len(), self.expected_results, &self.state);
            }
            ViewTab::Cache => {
                let chunks = ui::create_cache_layout(frame.area());
                
                ui::render_tab_bar(frame, chunks[0], &self.current_tab);
                let cached_searches = self.cache_manager.load().unwrap_or_default();
                
                // Render left sidebar with cache list
                ui::render_cache_sidebar(frame, chunks[1], &cached_searches, &mut self.cache_table_state);
                
                // Render center panel with results
                let selected_cache = self.cache_table_state.selected()
                    .and_then(|i| cached_searches.get(i));
                ui::render_cache_center_panel(
                    frame, 
                    chunks[2], 
                    selected_cache,
                    self.cache_result_table_state.selected()
                );
                
                ui::render_cache_status_bar(frame, chunks[3], cached_searches.len());
            }
        }
    }

    /// Handles keyboard events
    fn handle_key_event(&mut self, key: event::KeyEvent) -> Result<()> {
        // Global shortcuts
        match key.code {
            KeyCode::Char('q') | KeyCode::Esc => {
                self.should_quit = true;
                return Ok(());
            }
            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                self.should_quit = true;
                return Ok(());
            }
            KeyCode::Tab => {
                // Switch between search and cache views
                self.current_tab = match self.current_tab {
                    ViewTab::Search => ViewTab::Cache,
                    ViewTab::Cache => ViewTab::Search,
                };
                return Ok(());
            }
            KeyCode::Char('D') if key.modifiers.contains(KeyModifiers::SHIFT) => {
                // Shift+D to clear cache (only in cache view)
                if self.current_tab == ViewTab::Cache {
                    self.cache_manager.clear()?;
                    self.cache_table_state.select(None);
                }
                return Ok(());
            }
            _ => {}
        }

        // Tab-specific shortcuts
        match self.current_tab {
            ViewTab::Search => {
                match key.code {
                    KeyCode::Enter => {
                        // Trigger domain check instead of inserting newline
                        self.handle_search()?;
                        return Ok(());
                    }
                    KeyCode::Down => {
                        // Navigate results instead of moving cursor in textarea
                        self.handle_down();
                        return Ok(());
                    }
                    KeyCode::Up => {
                        // Navigate results instead of moving cursor in textarea
                        self.handle_up();
                        return Ok(());
                    }
                    KeyCode::Char('x') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                        // Ctrl+X for export (change from 'e' to avoid conflicts)
                        self.handle_export()?;
                        return Ok(());
                    }
                    _ => {}
                }
                // Let textarea handle all other keys (including Ctrl+U, Ctrl+W, etc.)
                self.search_input.input(key);
            }
            ViewTab::Cache => {
                match key.code {
                    KeyCode::Down => {
                        match self.cache_focus {
                            CacheFocus::Sidebar => {
                                self.handle_cache_down();
                            }
                            CacheFocus::Results => {
                                self.handle_cache_result_down();
                            }
                        }
                        return Ok(());
                    }
                    KeyCode::Up => {
                        match self.cache_focus {
                            CacheFocus::Sidebar => {
                                self.handle_cache_up();
                            }
                            CacheFocus::Results => {
                                self.handle_cache_result_up();
                            }
                        }
                        return Ok(());
                    }
                    KeyCode::Char('x') => {
                        // Delete selected cache entry (only works in sidebar)
                        if self.cache_focus == CacheFocus::Sidebar {
                            if let Some(index) = self.cache_table_state.selected() {
                                self.cache_manager.delete_by_index(index)?;
                                
                                // Adjust selection after deletion
                                let cache_count = self.cache_manager.load()?.len();
                                if cache_count == 0 {
                                    self.cache_table_state.select(None);
                                } else if index >= cache_count {
                                    self.cache_table_state.select(Some(cache_count.saturating_sub(1)));
                                }
                                
                                // Reset result selection
                                self.cache_result_table_state.select(None);
                                self.cache_focus = CacheFocus::Sidebar;
                            }
                        }
                        return Ok(());
                    }
                    KeyCode::Left => {
                        // Move focus to sidebar
                        self.cache_focus = CacheFocus::Sidebar;
                        self.cache_result_table_state.select(None);
                        return Ok(());
                    }
                    KeyCode::Right => {
                        // Move focus to results table
                        if self.cache_table_state.selected().is_some() {
                            self.cache_focus = CacheFocus::Results;
                            if self.cache_result_table_state.selected().is_none() {
                                self.cache_result_table_state.select(Some(0));
                            }
                        }
                        return Ok(());
                    }
                    _ => {}
                }
            }
        }

        Ok(())
    }

    /// Handles search submission
    fn handle_search(&mut self) -> Result<()> {
        let search_text = self.search_input.lines()[0].trim();
        if search_text.is_empty() {
            return Ok(());
        }

        // Store current query for caching later
        self.current_query = search_text.to_string();

        // Check if we have cached results for this query
        if let Ok(Some(cached)) = self.cache_manager.find_by_query(search_text) {
            // Use cached results
            self.results = cached.results;
            self.state = AppState::Results;
            self.expected_results = self.results.len();
            
            // Select first result if available
            if !self.results.is_empty() {
                self.table_state.select(Some(0));
            }
            
            return Ok(());
        }

        self.state = AppState::Searching;
        self.results.clear();
        self.table_state.select(None);

        // Create domains for all TLDs in preset
        let tlds = self.tld_preset.get_tlds();
        let domains: Vec<Domain> = tlds
            .iter()
            .filter_map(|tld| Domain::new(search_text, tld).ok())
            .collect();

        // Set expected results count
        self.expected_results = domains.len();

        // Create channel for receiving results
        let (tx, rx): (Sender<CheckResult>, Receiver<CheckResult>) = mpsc::channel();
        self.result_receiver = Some(rx);

        // Spawn async tasks to check domains concurrently in background
        std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().unwrap();
            
            runtime.block_on(async {
                let mut tasks = Vec::new();
                
                // Spawn concurrent tasks for all domains
                for domain in domains {
                    let rdap_client = RdapClient::new().unwrap();
                    let tx = tx.clone();
                    
                    let task = tokio::spawn(async move {
                        let result = rdap_client.check_domain(&domain).await;
                        match result {
                            Ok(check_result) => {
                                let _ = tx.send(check_result);
                            }
                            Err(_) => {
                                let error_result = CheckResult::error(
                                    domain.name.clone(),
                                    domain.tld.clone(),
                                    "Check failed".to_string(),
                                );
                                let _ = tx.send(error_result);
                            }
                        }
                    });
                    
                    tasks.push(task);
                }
                
                // Wait for all tasks to complete
                for task in tasks {
                    let _ = task.await;
                }
            });
        });

        Ok(())
    }

    /// Handles down arrow
    fn handle_down(&mut self) {
        if self.results.is_empty() {
            return;
        }

        let i = match self.table_state.selected() {
            Some(i) => {
                if i >= self.results.len() - 1 {
                    0
                } else {
                    i + 1
                }
            }
            None => 0,
        };
        self.table_state.select(Some(i));
    }

    /// Handles up arrow
    fn handle_up(&mut self) {
        if self.results.is_empty() {
            return;
        }

        let i = match self.table_state.selected() {
            Some(i) => {
                if i == 0 {
                    self.results.len() - 1
                } else {
                    i - 1
                }
            }
            None => 0,
        };
        self.table_state.select(Some(i));
    }

    /// Handles export to JSON
    fn handle_export(&self) -> Result<()> {
        if self.results.is_empty() {
            return Ok(());
        }

        let path = std::path::PathBuf::from("domain_results.json");
        export::export_json(&self.results, &path)?;
        
        Ok(())
    }

    /// Handles down arrow in cache view
    fn handle_cache_down(&mut self) {
        let cache_count = self.cache_manager.load().ok().map(|c| c.len()).unwrap_or(0);
        if cache_count == 0 {
            return;
        }

        let i = match self.cache_table_state.selected() {
            Some(i) => {
                if i >= cache_count - 1 {
                    0
                } else {
                    i + 1
                }
            }
            None => 0,
        };
        self.cache_table_state.select(Some(i));
    }

    /// Handles up arrow in cache view
    fn handle_cache_up(&mut self) {
        let cache_count = self.cache_manager.load().ok().map(|c| c.len()).unwrap_or(0);
        if cache_count == 0 {
            return;
        }

        let i = match self.cache_table_state.selected() {
            Some(i) => {
                if i == 0 {
                    cache_count - 1
                } else {
                    i - 1
                }
            }
            None => 0,
        };
        self.cache_table_state.select(Some(i));
    }

    /// Handles down arrow in cache results panel
    fn handle_cache_result_down(&mut self) {
        let cached_searches = self.cache_manager.load().unwrap_or_default();
        let selected_cache_index = self.cache_table_state.selected();
        
        if let Some(cache_index) = selected_cache_index {
            if let Some(cached_search) = cached_searches.get(cache_index) {
                let result_count = cached_search.results.len();
                if result_count == 0 {
                    return;
                }

                let i = match self.cache_result_table_state.selected() {
                    Some(i) => {
                        if i >= result_count - 1 {
                            0
                        } else {
                            i + 1
                        }
                    }
                    None => 0,
                };
                self.cache_result_table_state.select(Some(i));
            }
        }
    }

    /// Handles up arrow in cache results panel
    fn handle_cache_result_up(&mut self) {
        let cached_searches = self.cache_manager.load().unwrap_or_default();
        let selected_cache_index = self.cache_table_state.selected();
        
        if let Some(cache_index) = selected_cache_index {
            if let Some(cached_search) = cached_searches.get(cache_index) {
                let result_count = cached_search.results.len();
                if result_count == 0 {
                    return;
                }

                let i = match self.cache_result_table_state.selected() {
                    Some(i) => {
                        if i == 0 {
                            result_count - 1
                        } else {
                            i - 1
                        }
                    }
                    None => 0,
                };
                self.cache_result_table_state.select(Some(i));
            }
        }
    }
}
