use std::io::{self, BufReader, Read, Write};
use std::time::Duration;

use anyhow::{Context, Result};

use fyi_core::{EventQuery, Order, Storage};
use termion::async_stdin;
use termion::raw::IntoRawMode;
use termion::terminal_size;

use super::show::{calculate_details_width, print_event_row, print_table_header};

/// Default poll interval in milliseconds
const DEFAULT_POLL_MS: u64 = 500;

/// Default terminal height if we can't detect it (conservative to avoid flooding)
const DEFAULT_TERMINAL_HEIGHT: u16 = 20;

/// Options for the `watch` command.
///
/// Using a struct instead of individual parameters makes the API more maintainable
/// and prevents parameter order mistakes, especially with multiple `Option<String>` fields.
#[derive(Debug, Clone)]
pub struct WatchOptions {
    /// Filter by session ID
    pub session: Option<String>,
    /// Filter by event type
    pub event_type: Option<String>,
    /// Filter by permission mode
    pub permission_mode: Option<String>,
    /// Filter by framework (claude, gemini, cursor, etc.)
    pub framework: Option<String>,
    /// Poll interval in milliseconds (default: 500)
    pub poll_ms: u64,
}

impl Default for WatchOptions {
    fn default() -> Self {
        Self {
            session: None,
            event_type: None,
            permission_mode: None,
            framework: None,
            poll_ms: DEFAULT_POLL_MS,
        }
    }
}

impl WatchOptions {
    /// Create new options with default values.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the session filter.
    pub fn session(mut self, session: impl Into<Option<String>>) -> Self {
        self.session = session.into();
        self
    }

    /// Set the event type filter.
    pub fn event_type(mut self, event_type: impl Into<Option<String>>) -> Self {
        self.event_type = event_type.into();
        self
    }

    /// Set the permission mode filter.
    pub fn permission_mode(mut self, permission_mode: impl Into<Option<String>>) -> Self {
        self.permission_mode = permission_mode.into();
        self
    }

    /// Set the framework filter.
    pub fn framework(mut self, framework: impl Into<Option<String>>) -> Self {
        self.framework = framework.into();
        self
    }

    /// Set the poll interval in milliseconds.
    pub fn poll_ms(mut self, poll_ms: u64) -> Self {
        self.poll_ms = poll_ms;
        self
    }
}

/// Maximum initial events to display (prevents flooding on terminals that report huge heights)
const MAX_INITIAL_LINES: usize = 50;

/// Watch for new events and print them as they arrive
pub fn run_watch<S: Storage>(storage: &S, options: WatchOptions) -> Result<()> {
    // Always show the APP column
    let show_app = true;
    let details_width = calculate_details_width(show_app);

    // Print header
    print_table_header(show_app, details_width);
    io::stdout().flush().context("failed to flush stdout")?;

    // Display initial events to fill the terminal (minus 5 for header/separator/margin/footer)
    let term_height = terminal_size()
        .map(|(_, h)| h)
        .unwrap_or(DEFAULT_TERMINAL_HEIGHT) as usize;
    let initial_lines = term_height.saturating_sub(5).min(MAX_INITIAL_LINES);
    let mut last_event_id = 0i64;

    if initial_lines > 0 {
        // Query most recent events (including API requests)
        let initial_query = EventQuery::new()
            .with_session(options.session.clone())
            .with_event_type(options.event_type.clone())
            .with_permission_mode(options.permission_mode.clone())
            .with_framework(options.framework.clone())
            .with_direction(Order::Desc)
            .with_limit(initial_lines);

        let mut initial_events = storage
            .query(&initial_query)
            .context("failed to query initial events")?;

        // Track highest ID
        for event in &initial_events {
            if let Some(id) = event.id {
                last_event_id = last_event_id.max(id);
            }
        }

        // Reverse to show oldest first
        initial_events.reverse();

        for event in &initial_events {
            print_event_row(event, show_app, details_width, false);
        }
        io::stdout().flush().context("failed to flush stdout")?;
    }

    // Enable raw mode - the guard restores terminal on drop
    let _raw = io::stdout()
        .into_raw_mode()
        .context("failed to enable raw mode")?;

    // Get async stdin for non-blocking reads
    let mut stdin = BufReader::new(async_stdin()).bytes();

    loop {
        // Check for keyboard input (non-blocking)
        if let Some(Ok(b)) = stdin.next() {
            // Quit on 'q' or Ctrl+C (0x03)
            if b == b'q' || b == 0x03 {
                return Ok(());
            }
        }

        // Get new events after the last seen ID (includes API requests)
        let watch_query = EventQuery::new()
            .with_session(options.session.clone())
            .with_event_type(options.event_type.clone())
            .with_permission_mode(options.permission_mode.clone())
            .with_framework(options.framework.clone())
            .with_after_id(last_event_id)
            .with_limit(100);

        let new_events = storage
            .query(&watch_query)
            .context("failed to query events")?;

        // Track new highest ID and print new events
        for event in &new_events {
            if let Some(id) = event.id {
                last_event_id = last_event_id.max(id);
            }
            print_event_row(event, show_app, details_width, true);
            io::stdout().flush().context("failed to flush stdout")?;
        }

        // Sleep before polling again
        std::thread::sleep(Duration::from_millis(options.poll_ms));
    }
}
