use crate::config::Config;
use crate::state::{AppState, TimeItem};
use chrono::Utc;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use serde::Deserialize;
use std::process::Command;

#[derive(Debug, Deserialize, Clone)]
pub(crate) struct WatsonFrame {
    pub id: String,
    pub project: String,
    pub start: String,
    pub stop: String,
    pub tags: Vec<String>,
}

pub enum Screen {
    Main,
    Help,
    ConfigHelp,
    NewEntry,
    ReassignProject,
    LogView,
    LogViewHelp,
    AddAnnotation,
}

pub enum LogViewPeriod {
    Day,
    Week,
    Month,  // Represents the last 31 days
}

pub enum LogViewDayOrder {
    Chronological,      // Oldest first
    ReverseChronological, // Newest first (default)
}

#[derive(Debug, Copy, Clone)]
pub enum LogViewGrouping {
    ByDate,
    ByProject,
}

#[derive(Debug, Copy, Clone)]
pub enum LogViewSelection {
    Entry,      // Individual entry selected
    Project,    // Whole project selected (ByProject mode only)
    Day,        // Whole day selected
    All,        // Entire view/period selected
}

pub struct App {
    pub state: AppState,
    pub config: Config,
    pub current_screen: Screen,
    pub needs_clear: bool,
    pub new_entry_buffer: String,
    pub new_entry_project: String,
    pub new_entry_cursor: usize,
    pub new_entry_mode: NewEntryMode, // Task or Project
    pub reassign_project_buffer: String,
    pub reassign_project_cursor: usize,
    pub log_view_period: LogViewPeriod,
    pub log_view_grouping: LogViewGrouping,
    pub log_view_day_order: LogViewDayOrder,
    pub log_view_selection_level: LogViewSelection,
    pub log_view_frames: Vec<WatsonFrame>,
    pub log_view_content: Vec<String>,
    pub log_view_frame_indices: Vec<usize>, // Maps display order to frame index
    pub log_view_scroll: usize,
    pub log_view_selected: usize,
    pub log_view_rounded: bool, // Toggle for 15-minute rounding display
    pub help_scroll: usize,
    pub clipboard: Option<arboard::Clipboard>,
    pub status_message: Option<(String, std::time::Instant)>,
    pub annotation_buffer: String,
    pub annotation_cursor: usize,
}

pub enum NewEntryMode {
    Task,
    Project,
}

impl App {
    // Helper to round a time to the nearest 15-minute interval
    fn round_to_15min(dt: &chrono::DateTime<chrono::Local>) -> chrono::DateTime<chrono::Local> {
        use chrono::{Timelike, Duration};

        let minutes = dt.minute();
        let rounded_minutes = ((minutes + 7) / 15) * 15; // Round to nearest 15

        let mut rounded = dt.with_minute(0).unwrap().with_second(0).unwrap().with_nanosecond(0).unwrap();
        rounded = rounded + Duration::minutes(rounded_minutes as i64);

        // Handle hour overflow (e.g., 50 minutes rounds to 60)
        if rounded_minutes >= 60 {
            rounded = rounded.with_minute(0).unwrap();
        }

        rounded
    }

    // Helper to determine if a line is an entry line based on grouping mode
    pub fn is_entry_line(&self, line: &str) -> bool {
        // Exclude separator lines (time gap markers)
        if line.trim() == "---" {
            return false;
        }

        match self.log_view_grouping {
            LogViewGrouping::ByProject => line.starts_with("        "),
            LogViewGrouping::ByDate => line.starts_with("    ") && !line.starts_with("        "),
        }
    }

    fn ensure_watson_directories() -> anyhow::Result<()> {
        // Create watson config directory if it doesn't exist
        let watson_dir = dirs::config_dir()
            .ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?
            .join("watson");

        if !watson_dir.exists() {
            std::fs::create_dir_all(&watson_dir)?;
        }

        // Ensure frames file exists
        let frames_path = watson_dir.join("frames");
        if !frames_path.exists() {
            // Create an empty frames file (empty JSON array)
            std::fs::write(&frames_path, "[]")?;
        }

        Ok(())
    }

    pub fn new() -> anyhow::Result<Self> {
        // Ensure watson directories and files exist
        Self::ensure_watson_directories()?;

        let config = Config::load()?;
        let mut state = AppState::load()?;

        // Initialize current_pane to first enabled section
        let enabled = [
            config.show_permanent,
            config.show_recurring,
            config.show_recent,
        ];

        // Find first enabled pane
        if let Some(first_enabled) = enabled.iter().position(|&x| x) {
            state.current_pane = first_enabled;
        }

        Ok(Self {
            state,
            config,
            current_screen: Screen::Main,
            needs_clear: false,
            new_entry_buffer: String::new(),
            new_entry_project: String::new(),
            new_entry_cursor: 0,
            new_entry_mode: NewEntryMode::Task,
            reassign_project_buffer: String::new(),
            reassign_project_cursor: 0,
            log_view_period: LogViewPeriod::Day,
            log_view_grouping: LogViewGrouping::ByDate,
            log_view_day_order: LogViewDayOrder::ReverseChronological,
            log_view_selection_level: LogViewSelection::Entry,
            log_view_frames: Vec::new(),
            log_view_content: Vec::new(),
            log_view_frame_indices: Vec::new(),
            log_view_scroll: 0,
            log_view_selected: 0,
            log_view_rounded: false,
            help_scroll: 0,
            clipboard: arboard::Clipboard::new().ok(),
            status_message: None,
            annotation_buffer: String::new(),
            annotation_cursor: 0,
        })
    }

    pub fn handle_event(&mut self, event: Event) -> anyhow::Result<bool> {
        // Update status message
        self.update_status_message();

        let previous_screen = std::mem::discriminant(&self.current_screen);

        let result = match self.current_screen {
            Screen::Main => self.handle_main_event(event),
            Screen::Help => self.handle_help_event(event),
            Screen::ConfigHelp => self.handle_config_help_event(event),
            Screen::NewEntry => self.handle_new_entry_event(event),
            Screen::ReassignProject => self.handle_reassign_project_event(event),
            Screen::LogView => self.handle_log_view_event(event),
            Screen::LogViewHelp => self.handle_log_view_help_event(event),
            Screen::AddAnnotation => self.handle_add_annotation_event(event),
        };

        // If we switched screens, signal that we need to clear
        let current_screen = std::mem::discriminant(&self.current_screen);
        if previous_screen != current_screen {
            self.needs_clear = true;
        }

        result
    }

    fn handle_main_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent {
                code, modifiers, ..
            }) => match (code, modifiers) {
                (KeyCode::Char('q'), _) => return Ok(true),
                (KeyCode::Char('j'), _) | (KeyCode::Down, _) => self.move_selection(1),
                (KeyCode::Char('k'), _) | (KeyCode::Up, _) => self.move_selection(-1),
                (KeyCode::Char('h'), _) | (KeyCode::Left, _) => self.move_column(-1),
                (KeyCode::Char('l'), _) | (KeyCode::Right, _) => self.move_column(1),
                (KeyCode::Char('n'), KeyModifiers::CONTROL) => self.change_pane(1),
                (KeyCode::Char('p'), KeyModifiers::CONTROL) => self.change_pane(-1),
                (KeyCode::Enter, _) => self.toggle_current_item()?,
                (KeyCode::Char('e'), KeyModifiers::CONTROL) => self.edit_config()?,
                (KeyCode::Char('?'), _) => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::Help;
                }
                (KeyCode::Char('c'), _) => self.edit_app_config()?,
                (KeyCode::Char('n'), _) => self.start_new_entry(),
                (KeyCode::Char('p'), _) => self.start_reassign_project(),
                (KeyCode::Char('v'), _) => self.start_log_view()?,
                (KeyCode::Char('x'), _) => self.delete_current_item()?,
                _ => {}
            },
            _ => {}
        }
        Ok(false)
    }

    fn handle_new_entry_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent {
                code, modifiers, ..
            }) => {
                match (code, modifiers) {
                    (KeyCode::Esc, _) => {
                        self.current_screen = Screen::Main;
                        self.new_entry_buffer.clear();
                        self.new_entry_project.clear();
                        self.new_entry_mode = NewEntryMode::Task;
                        self.new_entry_cursor = 0;
                    }
                    (KeyCode::Enter, _) => {
                        match self.new_entry_mode {
                            NewEntryMode::Task => {
                                // Move from Tag to Project
                                self.new_entry_mode = NewEntryMode::Project;
                                self.new_entry_cursor = self.new_entry_buffer.len();
                            }
                            NewEntryMode::Project => {
                                // Project is required, tag is optional
                                if !self.new_entry_buffer.is_empty() {
                                    // Create new time item
                                    let item = TimeItem {
                                        name: self.new_entry_buffer.clone(),
                                        tags: if self.new_entry_project.is_empty() {
                                            vec![]
                                        } else {
                                            vec![self.new_entry_project.clone()]
                                        },
                                        last_used: Some(Utc::now()),
                                    };
                                    // Add to current pane (or recurring if in recent)
                                    match self.state.current_pane {
                                        0 => self.state.permanent_items.push(item), // Permanent Items
                                        1 => self.state.recurring_items.push(item), // Recurring Items
                                        2 => self.state.recent_items.push(item),    // Ad-Hoc Items
                                        _ => unreachable!(),
                                    }
                                    self.state.save()?;

                                    // Clear and return to main screen
                                    self.current_screen = Screen::Main;
                                    self.new_entry_buffer.clear();
                                    self.new_entry_project.clear();
                                    self.new_entry_mode = NewEntryMode::Task;
                                }
                            }
                        }
                    }
                    (KeyCode::Backspace, _) => match self.new_entry_mode {
                        NewEntryMode::Task => {
                            if self.new_entry_cursor > 0 {
                                self.new_entry_project.remove(self.new_entry_cursor - 1);
                                self.new_entry_cursor -= 1;
                            }
                        }
                        NewEntryMode::Project => {
                            if self.new_entry_cursor > 0 {
                                let idx = self.new_entry_cursor - 1;
                                if idx < self.new_entry_buffer.len() {
                                    self.new_entry_buffer.remove(idx);
                                    self.new_entry_cursor -= 1;
                                }
                            }
                        }
                    },
                    (KeyCode::Char(c), m) if m.is_empty() || m == KeyModifiers::SHIFT => match self.new_entry_mode {
                        NewEntryMode::Task => {
                            self.new_entry_project.insert(self.new_entry_cursor, c);
                            self.new_entry_cursor += 1;
                        }
                        NewEntryMode::Project => {
                            if self.new_entry_cursor <= self.new_entry_buffer.len() {
                                self.new_entry_buffer.insert(self.new_entry_cursor, c);
                                self.new_entry_cursor += 1;
                            }
                        }
                    },
                    _ => {}
                }
            }
            _ => {}
        }
        Ok(false)
    }

    fn handle_help_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent { code, .. }) => match code {
                KeyCode::Char('c') => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::ConfigHelp;
                }
                KeyCode::Char('j') | KeyCode::Down => {
                    self.help_scroll = self.help_scroll.saturating_add(1);
                }
                KeyCode::Char('k') | KeyCode::Up => {
                    self.help_scroll = self.help_scroll.saturating_sub(1);
                }
                KeyCode::PageDown => {
                    self.help_scroll = self.help_scroll.saturating_add(10);
                }
                KeyCode::PageUp => {
                    self.help_scroll = self.help_scroll.saturating_sub(10);
                }
                KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::Main;
                }
                _ => {}
            },
            _ => {}
        }
        Ok(false)
    }

    fn handle_config_help_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent { code, .. }) => match code {
                KeyCode::Char('j') | KeyCode::Down => {
                    self.help_scroll = self.help_scroll.saturating_add(1);
                }
                KeyCode::Char('k') | KeyCode::Up => {
                    self.help_scroll = self.help_scroll.saturating_sub(1);
                }
                KeyCode::PageDown => {
                    self.help_scroll = self.help_scroll.saturating_add(10);
                }
                KeyCode::PageUp => {
                    self.help_scroll = self.help_scroll.saturating_sub(10);
                }
                KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::Help;
                }
                _ => {}
            },
            _ => {}
        }
        Ok(false)
    }

    fn handle_log_view_help_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent { code, .. }) => match code {
                KeyCode::Char('j') | KeyCode::Down => {
                    self.help_scroll = self.help_scroll.saturating_add(1);
                }
                KeyCode::Char('k') | KeyCode::Up => {
                    self.help_scroll = self.help_scroll.saturating_sub(1);
                }
                KeyCode::PageDown => {
                    self.help_scroll = self.help_scroll.saturating_add(10);
                }
                KeyCode::PageUp => {
                    self.help_scroll = self.help_scroll.saturating_sub(10);
                }
                KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::LogView;
                }
                _ => {}
            },
            _ => {}
        }
        Ok(false)
    }

    fn handle_reassign_project_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent {
                code, modifiers, ..
            }) => {
                match (code, modifiers) {
                    (KeyCode::Esc, _) => {
                        self.current_screen = Screen::Main;
                        self.reassign_project_buffer.clear();
                        self.reassign_project_cursor = 0;
                    }
                    (KeyCode::Enter, _) => {
                        if self.reassign_project_buffer.is_empty()
                            || self.config.is_valid_project(&self.reassign_project_buffer)
                        {
                            self.reassign_project_for_current_item()?;
                            self.current_screen = Screen::Main;
                            self.reassign_project_buffer.clear();
                            self.reassign_project_cursor = 0;
                        }
                    }
                    (KeyCode::Backspace, _) => {
                        if self.reassign_project_cursor > 0 {
                            let idx = self.reassign_project_cursor - 1;
                            if idx < self.reassign_project_buffer.len() {
                                self.reassign_project_buffer.remove(idx);
                                self.reassign_project_cursor -= 1;
                            }
                        }
                    }
                    (KeyCode::Char(c), m) if m.is_empty() || m == KeyModifiers::SHIFT => {
                        if self.reassign_project_cursor <= self.reassign_project_buffer.len() {
                            self.reassign_project_buffer.insert(self.reassign_project_cursor, c);
                            self.reassign_project_cursor += 1;
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        Ok(false)
    }

    fn handle_log_view_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent { code, .. }) => match code {
                KeyCode::Esc | KeyCode::Char('q') => {
                    self.current_screen = Screen::Main;
                    self.log_view_scroll = 0;
                    self.log_view_selected = 0;
                    self.needs_clear = true;
                }
                KeyCode::Char('?') => {
                    self.help_scroll = 0;
                    self.current_screen = Screen::LogViewHelp;
                }
                KeyCode::Char('d') => {
                    self.log_view_period = LogViewPeriod::Day;
                    self.log_view_scroll = 0;
                    self.log_view_selected = 0;
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.load_log_content()?;
                    self.needs_clear = true;
                }
                KeyCode::Char('w') => {
                    self.log_view_period = LogViewPeriod::Week;
                    self.log_view_scroll = 0;
                    self.log_view_selected = 0;
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.load_log_content()?;
                    self.needs_clear = true;
                }
                KeyCode::Char('m') => {
                    self.log_view_period = LogViewPeriod::Month;
                    self.log_view_scroll = 0;
                    self.log_view_selected = 0;
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.load_log_content()?;
                    self.needs_clear = true;
                }
                KeyCode::Char('j') | KeyCode::Down => {
                    let old_selected = self.log_view_selected;
                    match self.log_view_selection_level {
                        LogViewSelection::Entry => {
                            // Move to next entry
                            if self.log_view_selected < self.log_view_frame_indices.len().saturating_sub(1) {
                                self.log_view_selected += 1;
                            }
                        }
                        LogViewSelection::Project => {
                            // Jump to next project group
                            self.jump_to_next_project();
                        }
                        LogViewSelection::Day => {
                            // Jump to next day
                            self.jump_to_next_day();
                        }
                        LogViewSelection::All => {
                            // No navigation at All level - already selecting everything
                        }
                    }
                    // Update scroll if selection changed
                    if self.log_view_selected != old_selected {
                        self.update_log_view_scroll();
                    }
                }
                KeyCode::Char('k') | KeyCode::Up => {
                    let old_selected = self.log_view_selected;
                    match self.log_view_selection_level {
                        LogViewSelection::Entry => {
                            // Move to previous entry
                            if self.log_view_selected > 0 {
                                self.log_view_selected -= 1;
                            }
                        }
                        LogViewSelection::Project => {
                            // Jump to previous project group
                            self.jump_to_previous_project();
                        }
                        LogViewSelection::Day => {
                            // Jump to previous day
                            self.jump_to_previous_day();
                        }
                        LogViewSelection::All => {
                            // No navigation at All level - already selecting everything
                        }
                    }
                    // Update scroll if selection changed
                    if self.log_view_selected != old_selected {
                        self.update_log_view_scroll();
                    }
                }
                KeyCode::Char('e') => {
                    // Only allow edit when selecting individual entry
                    if matches!(self.log_view_selection_level, LogViewSelection::Entry) {
                        self.edit_selected_frame()?;
                    }
                }
                KeyCode::Char('a') => {
                    // Only allow annotation when selecting individual entry
                    if matches!(self.log_view_selection_level, LogViewSelection::Entry) {
                        self.start_add_annotation();
                    }
                }
                KeyCode::Char('x') => {
                    // Only allow delete when selecting individual entry
                    if matches!(self.log_view_selection_level, LogViewSelection::Entry) {
                        self.delete_selected_frame()?;
                    }
                }
                KeyCode::Char('b') => {
                    // Backfill - adjust start time to match previous entry's end time
                    // Only works in ByDate mode at Entry level
                    if !matches!(self.log_view_selection_level, LogViewSelection::Entry) {
                        self.set_status_message("Backfill only works at Entry level (use h/l)");
                    } else if !matches!(self.log_view_grouping, LogViewGrouping::ByDate) {
                        self.set_status_message("Backfill only works in By Date view (press g)");
                    } else {
                        self.fix_entry_gap()?;
                    }
                }
                KeyCode::Char('h') | KeyCode::Left => {
                    // Zoom out selection level
                    let old_level = self.log_view_selection_level;
                    self.log_view_selection_level = match (&self.log_view_selection_level, &self.log_view_grouping) {
                        (LogViewSelection::All, _) => LogViewSelection::All, // Already at highest
                        (LogViewSelection::Day, _) => LogViewSelection::All,
                        (LogViewSelection::Project, _) => LogViewSelection::Day,
                        (LogViewSelection::Entry, LogViewGrouping::ByProject) => LogViewSelection::Project,
                        (LogViewSelection::Entry, LogViewGrouping::ByDate) => LogViewSelection::Day,
                    };
                }
                KeyCode::Char('l') | KeyCode::Right => {
                    // Zoom in selection level
                    let old_level = self.log_view_selection_level;
                    self.log_view_selection_level = match (&self.log_view_selection_level, &self.log_view_grouping) {
                        (LogViewSelection::Entry, _) => LogViewSelection::Entry, // Already at lowest
                        (LogViewSelection::Project, _) => LogViewSelection::Entry,
                        (LogViewSelection::Day, LogViewGrouping::ByProject) => LogViewSelection::Project,
                        (LogViewSelection::Day, LogViewGrouping::ByDate) => LogViewSelection::Entry,
                        (LogViewSelection::All, LogViewGrouping::ByProject) => LogViewSelection::Day,
                        (LogViewSelection::All, LogViewGrouping::ByDate) => LogViewSelection::Day,
                    };
                }
                KeyCode::Char('y') => {
                    self.copy_log_to_clipboard()?;
                }
                KeyCode::Char('g') => {
                    // Go to top entry
                    self.log_view_selected = 0;
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.update_log_view_scroll();
                }
                KeyCode::Char('G') => {
                    // Go to bottom entry
                    self.log_view_selected = self.log_view_frame_indices.len().saturating_sub(1);
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.update_log_view_scroll();
                }
                KeyCode::Char('p') => {
                    // Toggle grouping mode
                    self.log_view_grouping = match self.log_view_grouping {
                        LogViewGrouping::ByDate => LogViewGrouping::ByProject,
                        LogViewGrouping::ByProject => LogViewGrouping::ByDate,
                    };
                    self.log_view_selected = 0;
                    self.log_view_selection_level = LogViewSelection::Entry;
                    self.format_log_entries();
                    self.needs_clear = true;
                }
                KeyCode::Char('r') => {
                    // Toggle day order (chronological vs reverse chronological)
                    self.log_view_day_order = match self.log_view_day_order {
                        LogViewDayOrder::Chronological => LogViewDayOrder::ReverseChronological,
                        LogViewDayOrder::ReverseChronological => LogViewDayOrder::Chronological,
                    };
                    self.format_log_entries();
                    self.needs_clear = true;
                }
                KeyCode::Char('R') => {
                    // Toggle 15-minute rounding for display
                    self.log_view_rounded = !self.log_view_rounded;
                    self.format_log_entries();
                    self.needs_clear = true;
                }
                KeyCode::PageDown => {
                    self.log_view_selected = (self.log_view_selected + 10)
                        .min(self.log_view_frame_indices.len().saturating_sub(1));
                    self.update_log_view_scroll();
                }
                KeyCode::PageUp => {
                    self.log_view_selected = self.log_view_selected.saturating_sub(10);
                    self.update_log_view_scroll();
                }
                _ => {}
            },
            _ => {}
        }
        Ok(false)
    }

    fn format_log_entries(&mut self) {
        if self.log_view_frames.is_empty() {
            self.log_view_content = vec!["No log entries for this period.".to_string()];
            return;
        }

        match self.log_view_grouping {
            LogViewGrouping::ByDate => self.format_by_date(),
            LogViewGrouping::ByProject => self.format_by_project(),
        }

        // Update scroll position after formatting
        self.update_log_view_scroll();
    }

    fn format_by_date(&mut self) {
        use chrono::{DateTime, Local, Timelike};
        use std::collections::BTreeMap;

        // Group frames by date, tracking their original indices
        // Use YYYY-MM-DD as the key for proper sorting
        let mut by_date: BTreeMap<String, (String, Vec<(usize, &WatsonFrame)>)> = BTreeMap::new();

        // Choose date format based on view period
        let date_format = match self.log_view_period {
            LogViewPeriod::Month => "%d %B %Y",  // "23 November 2025" (no weekday)
            _ => "%A %d %B %Y",                   // "Saturday 23 November 2025"
        };

        for (idx, frame) in self.log_view_frames.iter().enumerate() {
            if let Ok(start_dt) = DateTime::parse_from_rfc3339(&frame.start) {
                let local_dt: DateTime<Local> = start_dt.into();
                let sort_key = local_dt.format("%Y-%m-%d").to_string(); // For sorting
                let display_date = local_dt.format(date_format).to_string(); // For display
                by_date
                    .entry(sort_key)
                    .or_insert_with(|| (display_date.clone(), Vec::new()))
                    .1
                    .push((idx, frame));
            }
        }

        let mut lines = Vec::new();
        let mut frame_indices = Vec::new();

        // Sort each day's frames chronologically
        for (_, frames) in by_date.values_mut() {
            frames.sort_by(|(_, a), (_, b)| a.start.cmp(&b.start));
        }

        // Collect dates in the desired order
        let dates: Vec<_> = match self.log_view_day_order {
            LogViewDayOrder::ReverseChronological => {
                by_date.iter().rev().collect()
            }
            LogViewDayOrder::Chronological => {
                by_date.iter().collect()
            }
        };

        for (_sort_key, (display_date, frames)) in dates {
            lines.push(display_date.clone());

            let mut prev_stop: Option<DateTime<Local>> = None;

            for (frame_idx, (idx, frame)) in frames.iter().enumerate() {
                if let (Ok(start_dt), Ok(stop_dt)) = (
                    DateTime::parse_from_rfc3339(&frame.start),
                    DateTime::parse_from_rfc3339(&frame.stop),
                ) {
                    let start_local: DateTime<Local> = start_dt.into();
                    let stop_local: DateTime<Local> = stop_dt.into();

                    // Apply rounding if enabled
                    let (display_start, display_stop) = if self.log_view_rounded {
                        (Self::round_to_15min(&start_local), Self::round_to_15min(&stop_local))
                    } else {
                        (start_local, stop_local)
                    };

                    // Check for time gap and add separator line if enabled
                    if self.config.show_time_gaps && frame_idx > 0 {
                        if let Some(prev) = prev_stop {
                            let gap = start_local.signed_duration_since(prev);
                            if gap.num_minutes() >= 5 {
                                lines.push("       ---".to_string());
                                // Note: don't add to frame_indices - separator is not an entry
                            }
                        }
                    }

                    let start_time = format!("{:02}:{:02}", display_start.hour(), display_start.minute());
                    let stop_time = format!("{:02}:{:02}", display_stop.hour(), display_stop.minute());

                    // Calculate duration (use rounded times if enabled)
                    let duration = display_stop.signed_duration_since(display_start);
                    let hours = duration.num_hours();
                    let minutes = duration.num_minutes() % 60;
                    let duration_str = if hours > 0 {
                        format!("({}h {:02}m)", hours, minutes)
                    } else {
                        format!("({}m)", minutes)
                    };

                    let display_text = if frame.tags.is_empty() {
                        frame.project.clone()
                    } else {
                        format!("{} [{}]", frame.tags.join(", "), frame.project)
                    };

                    let line_text = format!(
                        "    {} to {} {:>9}  {}",
                        start_time, stop_time, duration_str, display_text
                    );

                    lines.push(line_text);
                    frame_indices.push(*idx);  // Only add actual entries to frame_indices

                    prev_stop = Some(stop_local);
                }
            }
            lines.push(String::new()); // Empty line between dates
        }

        self.log_view_content = lines;
        self.log_view_frame_indices = frame_indices;
    }

    fn format_by_project(&mut self) {
        use chrono::{DateTime, Local, Timelike};
        use std::collections::BTreeMap;

        // Group frames by date, then by project within each date, tracking indices
        // Use YYYY-MM-DD as the key for proper sorting
        let mut by_date: BTreeMap<String, (String, BTreeMap<String, Vec<(usize, &WatsonFrame)>>)> = BTreeMap::new();

        // Choose date format based on view period
        let date_format = match self.log_view_period {
            LogViewPeriod::Month => "%d %B %Y",  // "23 November 2025" (no weekday)
            _ => "%A %d %B %Y",                   // "Saturday 23 November 2025"
        };

        for (idx, frame) in self.log_view_frames.iter().enumerate() {
            if let Ok(start_dt) = DateTime::parse_from_rfc3339(&frame.start) {
                let local_dt: DateTime<Local> = start_dt.into();
                let sort_key = local_dt.format("%Y-%m-%d").to_string(); // For sorting
                let display_date = local_dt.format(date_format).to_string(); // For display
                by_date
                    .entry(sort_key)
                    .or_insert_with(|| (display_date.clone(), BTreeMap::new()))
                    .1
                    .entry(frame.project.clone())
                    .or_insert_with(Vec::new)
                    .push((idx, frame));
            }
        }

        let mut lines = Vec::new();
        let mut frame_indices = Vec::new();

        // Sort frames within each project chronologically
        for (_, projects) in by_date.values_mut() {
            for frames in projects.values_mut() {
                frames.sort_by(|(_, a), (_, b)| a.start.cmp(&b.start));
            }
        }

        // Collect dates in the desired order
        let dates: Vec<_> = match self.log_view_day_order {
            LogViewDayOrder::ReverseChronological => {
                by_date.iter().rev().collect()
            }
            LogViewDayOrder::Chronological => {
                by_date.iter().collect()
            }
        };

        for (_sort_key, (display_date, projects)) in dates {
            lines.push(display_date.clone());

            for (project, frames) in projects.iter() {
                lines.push(format!("  {}", project)); // Project header with indent

                for (idx, frame) in frames {
                    if let (Ok(start_dt), Ok(stop_dt)) = (
                        DateTime::parse_from_rfc3339(&frame.start),
                        DateTime::parse_from_rfc3339(&frame.stop),
                    ) {
                        let start_local: DateTime<Local> = start_dt.into();
                        let stop_local: DateTime<Local> = stop_dt.into();

                        // Apply rounding if enabled
                        let (display_start, display_stop) = if self.log_view_rounded {
                            (Self::round_to_15min(&start_local), Self::round_to_15min(&stop_local))
                        } else {
                            (start_local, stop_local)
                        };

                        let start_time = format!("{:02}:{:02}", display_start.hour(), display_start.minute());
                        let stop_time = format!("{:02}:{:02}", display_stop.hour(), display_stop.minute());

                        // Calculate duration (use rounded times if enabled)
                        let duration = display_stop.signed_duration_since(display_start);
                        let hours = duration.num_hours();
                        let minutes = duration.num_minutes() % 60;
                        let duration_str = if hours > 0 {
                            format!("({}h {:02}m)", hours, minutes)
                        } else {
                            format!("({}m)", minutes)
                        };

                        let tags_str = if frame.tags.is_empty() {
                            String::new()
                        } else {
                            format!("  [{}]", frame.tags.join(", "))
                        };

                        lines.push(format!(
                            "        {} to {} {:>9}{}",
                            start_time, stop_time, duration_str, tags_str
                        ));
                        frame_indices.push(*idx);
                    }
                }
            }
            lines.push(String::new()); // Empty line between dates
        }

        self.log_view_content = lines;
        self.log_view_frame_indices = frame_indices;
    }

    fn edit_selected_frame(&mut self) -> anyhow::Result<()> {
        // Check if selection is valid
        if self.log_view_selected >= self.log_view_frame_indices.len() {
            return Ok(());
        }

        // Get the actual frame index from the display index
        let frame_idx = self.log_view_frame_indices[self.log_view_selected];
        if frame_idx >= self.log_view_frames.len() {
            return Ok(());
        }

        let frame_id = &self.log_view_frames[frame_idx].id;

        use crossterm::{
            execute,
            terminal::{disable_raw_mode, enable_raw_mode},
            terminal::{EnterAlternateScreen, LeaveAlternateScreen},
        };
        use std::io::stdout;

        // Leave TUI mode
        disable_raw_mode()?;
        execute!(stdout(), LeaveAlternateScreen)?;

        // Run watson edit
        let status = Command::new("watson").arg("edit").arg(frame_id).status()?;

        // Return to TUI mode
        execute!(stdout(), EnterAlternateScreen)?;
        enable_raw_mode()?;

        if status.success() {
            // Reload log content
            self.load_log_content()?;
        }

        self.needs_clear = true;

        Ok(())
    }

    fn delete_selected_frame(&mut self) -> anyhow::Result<()> {
        // Check if selection is valid
        if self.log_view_selected >= self.log_view_frame_indices.len() {
            return Ok(());
        }

        // Get the actual frame index from the display index
        let frame_idx = self.log_view_frame_indices[self.log_view_selected];
        if frame_idx >= self.log_view_frames.len() {
            return Ok(());
        }

        let frame_id = self.log_view_frames[frame_idx].id.clone();

        // Run watson remove with --force flag (no confirmation)
        let output = Command::new("watson")
            .arg("remove")
            .arg("--force")
            .arg(&frame_id)
            .output()?;

        if output.status.success() {
            // Reload log content
            self.load_log_content()?;

            // Adjust selection if we deleted the last item
            if self.log_view_selected >= self.log_view_frame_indices.len() && !self.log_view_frame_indices.is_empty() {
                self.log_view_selected = self.log_view_frame_indices.len() - 1;
            }
        }

        self.needs_clear = true;

        Ok(())
    }

    fn fix_entry_gap(&mut self) -> anyhow::Result<()> {
        use chrono::DateTime;
        use serde_json::Value;

        self.set_status_message("Backfill function called");

        // Check if selection is valid
        if self.log_view_selected >= self.log_view_frame_indices.len() {
            self.set_status_message("Invalid selection");
            return Ok(());
        }

        // Can't fix the first entry
        if self.log_view_selected == 0 {
            self.set_status_message("No previous entry!");
            return Ok(());
        }

        // Check if previous entry is on the same day
        let current_frame_idx = self.log_view_frame_indices[self.log_view_selected];
        let prev_frame_idx = self.log_view_frame_indices[self.log_view_selected - 1];

        if let (Some(current), Some(prev)) = (
            self.log_view_frames.get(current_frame_idx),
            self.log_view_frames.get(prev_frame_idx),
        ) {
            // Parse timestamps
            if let (Ok(curr_start), Ok(prev_stop)) = (
                DateTime::parse_from_rfc3339(&current.start),
                DateTime::parse_from_rfc3339(&prev.stop),
            ) {
                // Check if they're on the same day
                let curr_date = curr_start.format("%Y-%m-%d").to_string();
                let prev_date = prev_stop.format("%Y-%m-%d").to_string();

                if curr_date != prev_date {
                    self.set_status_message("No previous entry on same day!");
                    return Ok(());
                }

                // Read watson frames file
                let frames_path = dirs::config_dir()
                    .ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?
                    .join("watson")
                    .join("frames");

                let frames_content = std::fs::read_to_string(&frames_path)?;
                let mut frames: Value = serde_json::from_str(&frames_content)?;

                // Find and update the frame with matching id
                if let Some(frames_array) = frames.as_array_mut() {
                    for frame in frames_array {
                        if let Some(frame_array) = frame.as_array_mut() {
                            // Frame format: [start, stop, project, id, tags, ...]
                            if frame_array.len() > 3 {
                                if let Some(id) = frame_array[3].as_str() {
                                    if id == current.id {
                                        // Update start timestamp (index 0)
                                        let new_start_timestamp = prev_stop.timestamp();
                                        frame_array[0] = Value::Number(new_start_timestamp.into());
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                // Write back to frames file
                let updated_content = serde_json::to_string_pretty(&frames)?;
                std::fs::write(&frames_path, updated_content)?;

                // Reload log content
                self.load_log_content()?;
                self.set_status_message("Entry start time adjusted");
            }
        }

        self.needs_clear = true;
        Ok(())
    }

    fn copy_log_to_clipboard(&mut self) -> anyhow::Result<()> {
        if self.log_view_content.is_empty() {
            return Ok(());
        }

        // Determine what to copy based on selection level
        let text = match self.log_view_selection_level {
            LogViewSelection::Entry => {
                // Copy just the selected entry
                self.get_selected_entry_text()
            }
            LogViewSelection::Project => {
                // Copy the selected project group
                self.get_selected_project_text()
            }
            LogViewSelection::Day => {
                // Copy the entire day
                self.get_selected_day_text()
            }
            LogViewSelection::All => {
                // Copy the entire view/period
                self.log_view_content.join("\n")
            }
        };

        // Debug what we're copying
        std::fs::write("/tmp/wat_copy_debug.log", format!("Selection level: {:?}\nSelected index: {}\nText to copy:\n{}\n", self.log_view_selection_level, self.log_view_selected, text)).ok();

        // Copy to clipboard using the persistent clipboard instance
        if let Some(ref mut clipboard) = self.clipboard {
            if let Err(e) = clipboard.set_text(&text) {
                self.set_status_message(format!("Failed to copy: {}", e));
            } else {
                self.set_status_message(format!("Copied {} chars to clipboard", text.len()));
            }
        } else {
            // Try to create a new clipboard if we don't have one
            match arboard::Clipboard::new() {
                Ok(mut clipboard) => {
                    if let Err(e) = clipboard.set_text(&text) {
                        self.set_status_message(format!("Failed to copy: {}", e));
                    } else {
                        self.set_status_message(format!("Copied {} chars to clipboard", text.len()));
                    }
                    self.clipboard = Some(clipboard);
                }
                Err(e) => {
                    self.set_status_message(format!("Failed to access clipboard: {}", e));
                }
            }
        }

        Ok(())
    }

    fn get_selected_entry_text(&self) -> String {
        // Find the entry line corresponding to the selected frame
        let entry_lines: Vec<&String> = self
            .log_view_content
            .iter()
            .filter(|l| self.is_entry_line(l))
            .collect();

        if self.log_view_selected < entry_lines.len() {
            entry_lines[self.log_view_selected].to_string()
        } else {
            String::new()
        }
    }

    fn get_selected_project_text(&self) -> String {
        // Find all project headers and their entry ranges
        let mut projects = Vec::new(); // (first_entry_index, project_line_index)
        let mut entry_count = 0;
        let mut expecting_first_entry = false;
        let mut current_project_line_index = 0;

        for (line_index, line) in self.log_view_content.iter().enumerate() {
            if line.starts_with("  ") && !line.starts_with("   ") && !line.starts_with("\t") {
                // This is a project header
                current_project_line_index = line_index;
                expecting_first_entry = true;
            } else if self.is_entry_line(line) {
                if expecting_first_entry {
                    // This is the first entry of a project
                    projects.push((entry_count, current_project_line_index));
                    expecting_first_entry = false;
                }
                entry_count += 1;
            }
        }

        // Find which project contains our current selection
        let mut target_project_line_index = None;
        for i in 0..projects.len() {
            let project_start = projects[i].0;
            let project_end = if i + 1 < projects.len() {
                projects[i + 1].0
            } else {
                entry_count
            };

            if project_start <= self.log_view_selected && self.log_view_selected < project_end {
                target_project_line_index = Some(projects[i].1);
                break;
            }
        }

        if let Some(target_line_index) = target_project_line_index {
            // Collect all lines for the target project by position
            let mut project_lines = Vec::new();
            let mut in_target_project = false;
            let mut current_project_line_index = 0;

            for (line_index, line) in self.log_view_content.iter().enumerate() {
                if line.starts_with("  ") && !line.starts_with("   ") && !line.starts_with("\t") {
                    // This is a project header
                    current_project_line_index = line_index;
                    if line_index == target_line_index {
                        in_target_project = true;
                        project_lines.push(line.clone());
                    } else if in_target_project {
                        // Hit a different project header, we're done
                        break;
                    }
                } else if in_target_project && self.is_entry_line(line) {
                    // Entry within our target project
                    project_lines.push(line.clone());
                }
            }

            project_lines.join("\n")
        } else {
            String::new()
        }
    }

    fn get_selected_day_text(&self) -> String {
        // Find which day the selected entry belongs to
        let mut current_day_lines = Vec::new();
        let mut frame_count = 0;
        let mut found = false;

        for line in &self.log_view_content {
            if !line.starts_with('\t') && !line.is_empty() && !line.starts_with("  ") {
                // This is a date header
                if found {
                    break; // We've collected the target day
                }
                current_day_lines.clear();
                current_day_lines.push(line.clone());
            } else if line.starts_with('\t') || line.starts_with("  ") {
                // Add all lines within the day (project headers and entries)
                current_day_lines.push(line.clone());

                // Count only actual entries
                if self.is_entry_line(line) {
                    if frame_count == self.log_view_selected {
                        found = true;
                    }
                    frame_count += 1;
                }
            }
        }

        current_day_lines.join("\n")
    }

    fn jump_to_next_project(&mut self) {
        // Find all project headers and their first entry positions
        let mut projects = Vec::new(); // (first_entry_index)
        let mut entry_count = 0;
        let mut expecting_first_entry = false;

        for line in &self.log_view_content {
            if line.starts_with("  ") && !line.starts_with("   ") && !line.starts_with("\t") {
                // This is a project header - next entry will be first entry of this project
                expecting_first_entry = true;
            } else if self.is_entry_line(line) {
                if expecting_first_entry {
                    // This is the first entry of a project
                    projects.push(entry_count);
                    expecting_first_entry = false;
                }
                entry_count += 1;
            }
        }

        // Find which project contains our current selection and jump to next
        for i in 0..projects.len() {
            let project_start = projects[i];
            let project_end = if i + 1 < projects.len() {
                projects[i + 1]
            } else {
                entry_count
            };

            if project_start <= self.log_view_selected && self.log_view_selected < project_end {
                // Found current project, jump to next if it exists
                if i + 1 < projects.len() {
                    self.log_view_selected = projects[i + 1];
                }
                return;
            }
        }
    }

    fn jump_to_previous_project(&mut self) {
        // Find all project headers and their first entry positions
        let mut projects = Vec::new(); // (first_entry_index)
        let mut entry_count = 0;
        let mut expecting_first_entry = false;

        for line in &self.log_view_content {
            if line.starts_with("  ") && !line.starts_with("   ") && !line.starts_with("\t") {
                // This is a project header - next entry will be first entry of this project
                expecting_first_entry = true;
            } else if self.is_entry_line(line) {
                if expecting_first_entry {
                    // This is the first entry of a project
                    projects.push(entry_count);
                    expecting_first_entry = false;
                }
                entry_count += 1;
            }
        }

        // Find which project contains our current selection and jump to previous
        for i in 0..projects.len() {
            let project_start = projects[i];
            let project_end = if i + 1 < projects.len() {
                projects[i + 1]
            } else {
                entry_count
            };

            if project_start <= self.log_view_selected && self.log_view_selected < project_end {
                // Found current project, jump to previous if it exists
                if i > 0 {
                    self.log_view_selected = projects[i - 1];
                }
                return;
            }
        }
    }

    fn update_log_view_scroll(&mut self) {
        // Calculate which content line the selected entry is on
        let mut content_line: usize = 0;
        let mut frame_count = 0;
        for line in &self.log_view_content {
            if self.is_entry_line(line) {
                if frame_count == self.log_view_selected {
                    break;
                }
                frame_count += 1;
            }
            content_line += 1;
        }

        // Center the selection in the viewport
        // Assume a reasonable viewport height for centering calculation
        let viewport_center_offset: usize = 10; // Lines from top to center selection
        self.log_view_scroll = content_line.saturating_sub(viewport_center_offset);
    }

    fn jump_to_next_day(&mut self) {
        // Find all day headers and their first entry positions
        let mut days = Vec::new(); // (first_entry_index)
        let mut entry_count = 0;
        let mut current_day_seen = false;

        for line in &self.log_view_content {
            if !line.starts_with('\t') && !line.is_empty() && !line.starts_with("  ") {
                // This is a day header - mark that we'll record the next entry
                current_day_seen = true;
            } else if self.is_entry_line(line) && current_day_seen {
                // This is the first entry of a day
                days.push(entry_count);
                current_day_seen = false;
            }

            if self.is_entry_line(line) {
                entry_count += 1;
            }
        }

        // Find which day contains our current selection and jump to next
        for i in 0..days.len() {
            let day_start = days[i];
            let day_end = if i + 1 < days.len() {
                days[i + 1]
            } else {
                entry_count
            };

            if day_start <= self.log_view_selected && self.log_view_selected < day_end {
                // Found current day, jump to next if it exists
                if i + 1 < days.len() {
                    self.log_view_selected = days[i + 1];
                }
                return;
            }
        }
    }

    fn jump_to_previous_day(&mut self) {
        // Find all day headers and their first entry positions
        let mut days = Vec::new(); // (first_entry_index)
        let mut entry_count = 0;
        let mut current_day_seen = false;

        for line in &self.log_view_content {
            if !line.starts_with('\t') && !line.is_empty() && !line.starts_with("  ") {
                // This is a day header - mark that we'll record the next entry
                current_day_seen = true;
            } else if self.is_entry_line(line) && current_day_seen {
                // This is the first entry of a day
                days.push(entry_count);
                current_day_seen = false;
            }

            if self.is_entry_line(line) {
                entry_count += 1;
            }
        }

        // Find which day contains our current selection and jump to previous
        for i in 0..days.len() {
            let day_start = days[i];
            let day_end = if i + 1 < days.len() {
                days[i + 1]
            } else {
                entry_count
            };

            if day_start <= self.log_view_selected && self.log_view_selected < day_end {
                // Found current day, jump to previous if it exists
                if i > 0 {
                    self.log_view_selected = days[i - 1];
                }
                return;
            }
        }
    }

    fn move_selection(&mut self, delta: i32) {
        let items = match self.state.current_pane {
            0 => &self.state.permanent_items,
            1 => &self.state.recurring_items,
            2 => &self.state.recent_items,
            _ => return,
        };

        if items.is_empty() {
            return;
        }

        let current = self.state.selected_indices[self.state.current_pane] as i32;
        let items_len = items.len() as i32;

        // Check if we're using two-column mode
        let use_two_columns = self.config.multi_column && items.len() > 6;

        if use_two_columns {
            let mid_point = (items.len() + 1) / 2;
            let (col_start, col_end) = if self.state.current_column == 0 {
                (0, mid_point as i32 - 1)
            } else {
                (mid_point as i32, items_len - 1)
            };

            let new_index = current + delta;

            if new_index < col_start && delta < 0 {
                // Moving up beyond column - go to previous pane
                self.change_pane(-1);
                let new_items = match self.state.current_pane {
                    0 => &self.state.permanent_items,
                    1 => &self.state.recurring_items,
                    2 => &self.state.recent_items,
                    _ => return,
                };
                if !new_items.is_empty() {
                    self.state.selected_indices[self.state.current_pane] = new_items.len() - 1;
                    // Stay in right column if new pane has enough items
                    if self.config.multi_column && new_items.len() > 6 {
                        self.state.current_column = 1;
                    } else {
                        self.state.current_column = 0;
                    }
                }
            } else if new_index > col_end && delta > 0 {
                // Moving down beyond column - go to next pane
                self.change_pane(1);
                self.state.selected_indices[self.state.current_pane] = 0;
                self.state.current_column = 0;
            } else {
                // Normal movement within column
                self.state.selected_indices[self.state.current_pane] =
                    new_index.clamp(col_start, col_end) as usize;
            }
        } else {
            // Single column mode
            let new_index = current + delta;

            if new_index < 0 && delta < 0 {
                // At top, move to previous pane
                self.change_pane(-1);
                let new_items = match self.state.current_pane {
                    0 => &self.state.permanent_items,
                    1 => &self.state.recurring_items,
                    2 => &self.state.recent_items,
                    _ => return,
                };
                if !new_items.is_empty() {
                    self.state.selected_indices[self.state.current_pane] = new_items.len() - 1;
                }
            } else if new_index >= items_len && delta > 0 {
                // At bottom, move to next pane
                self.change_pane(1);
                self.state.selected_indices[self.state.current_pane] = 0;
            } else {
                // Normal movement within pane
                self.state.selected_indices[self.state.current_pane] =
                    new_index.clamp(0, items_len - 1) as usize;
            }
        }
    }

    fn move_column(&mut self, delta: i32) {
        if !self.config.multi_column {
            return;
        }

        let items = match self.state.current_pane {
            0 => &self.state.permanent_items,
            1 => &self.state.recurring_items,
            2 => &self.state.recent_items,
            _ => return,
        };

        // Only switch columns if we have enough items for two columns
        if items.len() <= 6 {
            return;
        }

        let mid_point = (items.len() + 1) / 2;
        let new_column = if delta > 0 { 1 } else { 0 };

        if new_column != self.state.current_column {
            let current_selected = self.state.selected_indices[self.state.current_pane];

            // Calculate which row we're on in the current column
            let current_row = if self.state.current_column == 0 {
                current_selected  // In left column
            } else {
                current_selected.saturating_sub(mid_point)  // In right column
            };

            // Switch column and jump to the same row in the new column
            self.state.current_column = new_column;

            if new_column == 0 {
                // Moving to left column - jump to same row
                self.state.selected_indices[self.state.current_pane] = current_row.min(mid_point - 1);
            } else {
                // Moving to right column - jump to same row
                let right_items_len = items.len() - mid_point;
                self.state.selected_indices[self.state.current_pane] = (mid_point + current_row).min(items.len() - 1).min(mid_point + right_items_len - 1);
            }
        }
    }

    fn change_pane(&mut self, delta: i32) {
        // Find next enabled pane
        let mut next_pane = self.state.current_pane;
        let enabled = [
            self.config.show_permanent,
            self.config.show_recurring,
            self.config.show_recent,
        ];

        // Count enabled panes
        let enabled_count = enabled.iter().filter(|&&x| x).count();
        if enabled_count == 0 {
            return; // No panes to switch to
        }

        // Find next enabled pane
        for _ in 0..3 {
            next_pane = ((next_pane as i32 + delta).rem_euclid(3)) as usize;
            if enabled[next_pane] {
                break;
            }
        }

        self.state.current_pane = next_pane;
        self.state.current_column = 0;
    }

    fn get_current_item(&self) -> Option<TimeItem> {
        let items = match self.state.current_pane {
            0 => &self.state.permanent_items,
            1 => &self.state.recurring_items,
            2 => &self.state.recent_items,
            _ => return None,
        };

        let index = self.state.selected_indices[self.state.current_pane];
        items.get(index).cloned()
    }

    fn toggle_current_item(&mut self) -> anyhow::Result<()> {
        if let Some(item) = self.get_current_item() {
            if self
                .state
                .active_timer
                .as_ref()
                .map(|(active, _)| active.name == item.name)
                .unwrap_or(false)
            {
                self.state.stop_timer()?;
            } else {
                self.state.start_timer(item)?;
            }
            self.state.save()?;
        }
        Ok(())
    }

    fn delete_current_item(&mut self) -> anyhow::Result<()> {
        // Check if this is the active timer
        let should_stop = {
            let items = match self.state.current_pane {
                0 => &self.state.permanent_items,
                1 => &self.state.recurring_items,
                2 => &self.state.recent_items,
                _ => return Ok(()),
            };

            let index = self.state.selected_indices[self.state.current_pane];

            if !items.is_empty() && index < items.len() {
                if let Some((ref active, _)) = self.state.active_timer {
                    items[index].name == active.name
                } else {
                    false
                }
            } else {
                return Ok(());
            }
        };

        // Stop timer if needed
        if should_stop {
            self.state.stop_timer()?;
        }

        // Now delete the item
        let items = match self.state.current_pane {
            0 => &mut self.state.permanent_items,
            1 => &mut self.state.recurring_items,
            2 => &mut self.state.recent_items,
            _ => return Ok(()),
        };

        let index = self.state.selected_indices[self.state.current_pane];

        if !items.is_empty() && index < items.len() {
            // Remove the item
            items.remove(index);

            // Adjust index if we're at the end
            if !items.is_empty() && index == items.len() {
                self.state.selected_indices[self.state.current_pane] = items.len() - 1;
            }

            // Save changes
            self.state.save()?;
        }

        Ok(())
    }

    fn set_status_message<S: Into<String>>(&mut self, message: S) {
        self.status_message = Some((message.into(), std::time::Instant::now()));
    }

    fn update_status_message(&mut self) {
        // Clear status message after 3 seconds
        if let Some((_, instant)) = self.status_message {
            if instant.elapsed().as_secs() >= 3 {
                self.status_message = None;
            }
        }
    }

    fn start_new_entry(&mut self) {
        self.current_screen = Screen::NewEntry;
        self.new_entry_buffer.clear();
        self.new_entry_project.clear();
        self.new_entry_cursor = 0;
        self.new_entry_mode = NewEntryMode::Task;
    }

    fn start_reassign_project(&mut self) {
        if let Some(item) = self.get_current_item() {
            self.current_screen = Screen::ReassignProject;
            // Pre-fill with current project (the name field)
            self.reassign_project_buffer = item.name.clone();
            self.reassign_project_cursor = self.reassign_project_buffer.len();
        }
    }

    fn start_log_view(&mut self) -> anyhow::Result<()> {
        self.current_screen = Screen::LogView;
        self.log_view_period = LogViewPeriod::Day;
        self.log_view_scroll = 0;
        self.load_log_content()?;
        Ok(())
    }

    fn load_log_content(&mut self) -> anyhow::Result<()> {
        let mut command = Command::new("watson");
        command.arg("log").arg("--json");

        // Handle different period options
        match self.log_view_period {
            LogViewPeriod::Day => {
                command.arg("--day");
            },
            LogViewPeriod::Week => {
                command.arg("--week");
            },
            LogViewPeriod::Month => {
                // Use --from with date 31 days ago for month view
                let thirty_one_days_ago = chrono::Local::now()
                    .checked_sub_signed(chrono::Duration::days(31))
                    .expect("Failed to calculate date from 31 days ago");

                command.arg("--from");
                command.arg(thirty_one_days_ago.format("%Y-%m-%d").to_string());
            },
        };

        let output = command.output()?;

        if output.status.success() {
            let json_str = String::from_utf8_lossy(&output.stdout);

            // Parse JSON frames
            match serde_json::from_str::<Vec<WatsonFrame>>(&json_str) {
                Ok(frames) => {
                    self.log_view_frames = frames;
                    self.format_log_entries();
                }
                Err(e) => {
                    self.log_view_frames.clear();
                    self.log_view_content = vec![
                        "Failed to parse Watson log JSON:".to_string(),
                        e.to_string(),
                    ];
                }
            }
        } else {
            let error = String::from_utf8_lossy(&output.stderr);
            self.log_view_frames.clear();
            self.log_view_content = vec![
                "Failed to load Watson log:".to_string(),
                error.to_string(),
            ];
        }

        Ok(())
    }

    fn reassign_project_for_current_item(&mut self) -> anyhow::Result<()> {
        let items = match self.state.current_pane {
            0 => &mut self.state.permanent_items,
            1 => &mut self.state.recurring_items,
            2 => &mut self.state.recent_items,
            _ => return Ok(()),
        };

        let index = self.state.selected_indices[self.state.current_pane];

        let needs_restart = if let Some(item) = items.get_mut(index) {
            // Update the project name (item.name field)
            let old_name = item.name.clone();
            item.name = self.reassign_project_buffer.clone();

            // Check if this is the active timer
            self.state
                .active_timer
                .as_ref()
                .map(|(active, _)| active.name == old_name)
                .unwrap_or(false)
        } else {
            false
        };

        // If this was the active timer, restart it with new project name
        if needs_restart {
            let item = items[index].clone();
            self.state.stop_timer()?;
            self.state.start_timer(item)?;
        }

        self.state.save()?;
        Ok(())
    }

    fn edit_app_config(&mut self) -> anyhow::Result<()> {
        use crossterm::{
            execute,
            terminal::{disable_raw_mode, enable_raw_mode},
            terminal::{EnterAlternateScreen, LeaveAlternateScreen},
        };
        use std::io::stdout;

        let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
        let config_path = Config::config_path()?;

        // Leave TUI mode
        disable_raw_mode()?;
        execute!(stdout(), LeaveAlternateScreen)?;

        // Run editor
        let status = Command::new(editor).arg(&config_path).status()?;

        if status.success() {
            // Reload entire application state
            self.config = Config::load()?;
            self.current_screen = Screen::Main;
        }

        // Return to TUI mode
        execute!(stdout(), EnterAlternateScreen)?;
        enable_raw_mode()?;

        self.needs_clear = true;

        Ok(())
    }

    fn edit_config(&mut self) -> anyhow::Result<()> {
        use crossterm::{
            execute,
            terminal::{disable_raw_mode, enable_raw_mode},
            terminal::{EnterAlternateScreen, LeaveAlternateScreen},
        };
        use std::io::stdout;

        let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
        let config_path = AppState::config_file()?;

        if !config_path.exists() {
            std::fs::write(&config_path, "# WAT Configuration\n# Add your permanent items here\n\npermanent_items:\n  - name: Daily Standup\n    tags: [daily, meeting]\n")?;
        }

        // Leave TUI mode
        disable_raw_mode()?;
        execute!(stdout(), LeaveAlternateScreen)?;

        // Run editor
        let status = Command::new(editor).arg(&config_path).status()?;

        if status.success() {
            // Reload entire application state
            self.state = AppState::load()?;
            self.current_screen = Screen::Main;
        }

        // Return to TUI mode
        execute!(stdout(), EnterAlternateScreen)?;
        enable_raw_mode()?;

        self.needs_clear = true;

        Ok(())
    }

    fn start_add_annotation(&mut self) {
        self.current_screen = Screen::AddAnnotation;
        self.annotation_buffer.clear();
        self.annotation_cursor = 0;
    }

    fn handle_add_annotation_event(&mut self, event: Event) -> anyhow::Result<bool> {
        match event {
            Event::Key(KeyEvent {
                code, modifiers, ..
            }) => {
                match (code, modifiers) {
                    (KeyCode::Esc, _) => {
                        self.current_screen = Screen::LogView;
                        self.annotation_buffer.clear();
                        self.annotation_cursor = 0;
                    }
                    (KeyCode::Enter, _) => {
                        if !self.annotation_buffer.is_empty() {
                            self.add_annotation_to_frame()?;
                        }
                        self.current_screen = Screen::LogView;
                        self.annotation_buffer.clear();
                        self.annotation_cursor = 0;
                    }
                    (KeyCode::Backspace, _) => {
                        if self.annotation_cursor > 0 {
                            let idx = self.annotation_cursor - 1;
                            if idx < self.annotation_buffer.len() {
                                self.annotation_buffer.remove(idx);
                                self.annotation_cursor -= 1;
                            }
                        }
                    }
                    (KeyCode::Char(c), m) if m.is_empty() || m == KeyModifiers::SHIFT => {
                        if self.annotation_cursor <= self.annotation_buffer.len() {
                            self.annotation_buffer.insert(self.annotation_cursor, c);
                            self.annotation_cursor += 1;
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        Ok(false)
    }

    fn add_annotation_to_frame(&mut self) -> anyhow::Result<()> {
        use serde_json::Value;

        // Check if selection is valid
        if self.log_view_selected >= self.log_view_frame_indices.len() {
            self.set_status_message("Invalid selection");
            return Ok(());
        }

        // Get the actual frame index from the display index
        let frame_idx = self.log_view_frame_indices[self.log_view_selected];
        if frame_idx >= self.log_view_frames.len() {
            return Ok(());
        }

        let frame_id = self.log_view_frames[frame_idx].id.clone();

        // Read watson frames file
        let frames_path = dirs::config_dir()
            .ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?
            .join("watson")
            .join("frames");

        let frames_content = std::fs::read_to_string(&frames_path)?;
        let mut frames: Value = serde_json::from_str(&frames_content)?;

        // Find and update the frame with matching id
        if let Some(frames_array) = frames.as_array_mut() {
            for frame in frames_array {
                if let Some(frame_array) = frame.as_array_mut() {
                    // Frame format: [start, stop, project, id, tags, updated]
                    if frame_array.len() > 4 {
                        if let Some(id) = frame_array[3].as_str() {
                            if id == frame_id {
                                // Update tags array (index 4)
                                if let Some(tags) = frame_array[4].as_array_mut() {
                                    // Add the annotation tag
                                    tags.push(Value::String(self.annotation_buffer.clone()));
                                }
                                // Update timestamp (index 5)
                                if frame_array.len() > 5 {
                                    frame_array[5] = Value::Number(chrono::Utc::now().timestamp().into());
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        // Write back to frames file
        let updated_content = serde_json::to_string_pretty(&frames)?;
        std::fs::write(&frames_path, updated_content)?;

        // Reload log content
        self.load_log_content()?;
        self.set_status_message("Annotation added");

        self.needs_clear = true;
        Ok(())
    }
}
