use crate::core::model::{Category, ColorTag, State};
use crate::core::ops;
use crate::core::store::Store;
use chrono::Local;

use crate::tui::{keymap, ui};

use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::io;


#[derive(Debug, Clone)]
pub enum RowRef {
    Header(Category),
    Item { category: Category, id: u64, depth: usize },
}

#[derive(Debug, PartialEq, Eq)]
pub enum InputMode {
    Normal,
    Popup(PopupState),
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FocusedField {
    Title,
    Notes,
    Created,
    Updated,
}

#[derive(Debug, PartialEq, Eq)]
pub enum PopupState {
    AddTodo { 
        category: Category, 
        input: String, 
        notes: String, 
        created_at: i64,
        updated_at: i64,
        focus: FocusedField,
        date_unit: usize, // 0:Y, 1:M, 2:D, 3:H, 4:M, 5:S
    },
    EditTodo { 
        id: u64, 
        input: String, 
        notes: String, 
        created_at: i64,
        updated_at: i64,
        focus: FocusedField,
        date_unit: usize,
    },
    AddSub { 
        todo_id: u64, 
        input: String, 
        notes: String, 
        created_at: i64,
        updated_at: i64,
        focus: FocusedField,
        date_unit: usize,
    },
    EditSub { 
        todo_id: u64, 
        sub_id: u64, 
        input: String, 
        notes: String, 
        created_at: i64,
        updated_at: i64,
        focus: FocusedField,
        date_unit: usize,
    },
    Move { id: u64, current_selection: usize }, // Selection 0-3 for categories
    DeleteConfirm { id: u64, is_sub: bool }, // Generic delete confirmation
    Message { text: String },
}

pub struct App {
    pub state: State,
    pub store: Box<dyn Store>,
    pub rows: Vec<RowRef>,
    pub cursor: usize,
    pub scroll: usize,
    pub mode: InputMode,
    pub show_notes: bool,
    pub exit: bool,
}

impl App {
    pub fn new(store: Box<dyn Store>) -> anyhow::Result<Self> {
        let state = store.load()?;
        let mut app = Self {
            state,
            store,
            rows: Vec::new(),
            cursor: 0,
            scroll: 0,
            mode: InputMode::Normal,
            show_notes: false,
            exit: false,
        };

        app.rebuild_rows();
        Ok(app)
    }

    pub fn rebuild_rows(&mut self) {
        self.rows.clear();
        for cat in State::all_categories() {
            self.rows.push(RowRef::Header(cat));
            // Collect and sort todos by created_at descending
            let mut todos: Vec<_> = self.state.get_category(cat).iter().cloned().collect();
            todos.sort_by(|a, b| b.created_at.cmp(&a.created_at));
            
            for todo in todos {
                self.rows.push(RowRef::Item { category: cat, id: todo.id, depth: 0 });
                self.add_subs_recursive(&todo.subtasks, cat, 1);
            }
        }
        // Adjust cursor if out of bounds
        if self.cursor >= self.rows.len() && !self.rows.is_empty() {
            self.cursor = self.rows.len() - 1;
        }
    }

    fn add_subs_recursive(&mut self, subs: &[crate::core::model::SubTask], cat: Category, depth: usize) {
        let mut sorted_subs = subs.to_vec();
        sorted_subs.sort_by(|a, b| b.created_at.cmp(&a.created_at));
        for sub in sorted_subs {
            self.rows.push(RowRef::Item { category: cat, id: sub.id, depth });
            if !sub.subtasks.is_empty() {
                self.add_subs_recursive(&sub.subtasks, cat, depth + 1);
            }
        }
    }


    pub fn run(&mut self) -> anyhow::Result<()> {
        enable_raw_mode()?;
        let mut stdout = io::stdout();
        execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
        let backend = CrosstermBackend::new(stdout);
        let mut terminal = Terminal::new(backend)?;

        let res = self.run_loop(&mut terminal);

        disable_raw_mode()?;
        execute!(
            terminal.backend_mut(),
            LeaveAlternateScreen,
            DisableMouseCapture
        )?;
        terminal.show_cursor()?;

        if let Err(err) = res {
            println!("{:?}", err);
        }

        Ok(())
    }

    fn run_loop(&mut self, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> anyhow::Result<()> {
        while !self.exit {
            terminal.draw(|f| {
                ui::draw(f, self);
                // After draw, check visibility for next frame?
                // Actually `ensure_visible` needs viewport height, which we get in draw.
                // But we don't want to mutate self in draw.
                // We can cache height or pass it out. simpler: specific ensure_visible call if we knew height.
                // For now, let's assume height ~ term height - overhead.
                // UI module handles the scrolling Viewport calculation usually,
                // but we explicitly manage scroll index in App.
            })?;

            if event::poll(std::time::Duration::from_millis(100))? {
                match event::read()? {
                    Event::Key(key) => self.handle_key(key)?,
                    Event::Mouse(mouse) => self.handle_mouse(mouse)?,
                    _ => {}
                }
            }
        }
        Ok(())
    }

    fn handle_key(&mut self, key: event::KeyEvent) -> anyhow::Result<()> {
        match &mut self.mode {
            InputMode::Normal => self.handle_normal(key),
            InputMode::Popup(_) => self.handle_popup(key),
        }
    }

    fn handle_mouse(&mut self, mouse: event::MouseEvent) -> anyhow::Result<()> {
        match mouse.kind {
            event::MouseEventKind::ScrollDown => {
                match &mut self.mode {
                    InputMode::Normal => self.perform_action(keymap::Action::Down)?,
                    InputMode::Popup(state) => match state {
                        PopupState::Move { current_selection, .. } => {
                            *current_selection = (*current_selection + 1).min(3);
                        }
                        PopupState::AddTodo { focus, .. } |
                        PopupState::EditTodo { focus, .. } |
                        PopupState::AddSub { focus, .. } |
                        PopupState::EditSub { focus, .. } => {
                            *focus = FocusedField::Notes;
                        }
                        _ => {}
                    }
                }
            }
            event::MouseEventKind::ScrollUp => {
                match &mut self.mode {
                    InputMode::Normal => self.perform_action(keymap::Action::Up)?,
                    InputMode::Popup(state) => match state {
                        PopupState::Move { current_selection, .. } => {
                            *current_selection = current_selection.saturating_sub(1);
                        }
                        PopupState::AddTodo { focus, .. } |
                        PopupState::EditTodo { focus, .. } |
                        PopupState::AddSub { focus, .. } |
                        PopupState::EditSub { focus, .. } => {
                            *focus = FocusedField::Title;
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
        Ok(())
    }

    fn handle_normal(&mut self, key: event::KeyEvent) -> anyhow::Result<()> {
        let action = keymap::map_key(key);
        self.perform_action(action)
    }

    fn perform_action(&mut self, action: keymap::Action) -> anyhow::Result<()> {
        match action {
            keymap::Action::Quit => self.exit = true,
            keymap::Action::Down => {
                if !self.rows.is_empty() {
                    self.cursor = (self.cursor + 1).min(self.rows.len() - 1);
                    // Estimate viewport
                    let term_height = crossterm::terminal::size()?.1 as usize;
                    let list_height = term_height.saturating_sub(2);
                    if self.cursor >= self.scroll + list_height {
                        self.scroll = self.cursor + 1 - list_height;
                    }
                }
            }
            keymap::Action::Up => {
                self.cursor = self.cursor.saturating_sub(1);
                if self.cursor < self.scroll {
                    self.scroll = self.cursor;
                }
            }
            keymap::Action::Top => {
                self.cursor = 0;
                self.scroll = 0;
            }
            keymap::Action::Bottom => {
                if !self.rows.is_empty() {
                    self.cursor = self.rows.len() - 1;
                    self.scroll = self.rows.len().saturating_sub(1);
                }
            }
            keymap::Action::ReorderUp => {
                if let Some(RowRef::Item { id, .. }) = self.rows.get(self.cursor) {
                    ops::reorder_item(&self.store, *id, true)?;
                    self.reload()?;
                }
            }
             keymap::Action::ReorderDown => {
                if let Some(RowRef::Item { id, .. }) = self.rows.get(self.cursor) {
                    ops::reorder_item(&self.store, *id, false)?;
                    self.reload()?;
                }
            }
            keymap::Action::Add => {
                let cat = self.get_current_category().unwrap_or(Category::Short);
                let now = Local::now().timestamp();
                self.mode = InputMode::Popup(PopupState::AddTodo { 
                    category: cat, 
                    input: String::new(), 
                    notes: String::new(), 
                    created_at: now,
                    updated_at: now,
                    focus: FocusedField::Title,
                    date_unit: 0,
                });
            }
            keymap::Action::Edit => {
                 if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                     if *depth == 0 {
                          if let Some(todo) = self.find_todo(*id) {
                              self.mode = InputMode::Popup(PopupState::EditTodo { 
                                 id: *id, 
                                 input: todo.title.clone(), 
                                 notes: todo.notes.clone().unwrap_or_default(), 
                                 created_at: todo.created_at,
                                 updated_at: todo.updated_at,
                                 focus: FocusedField::Title,
                                 date_unit: 0,
                             });
                          }
                     } else {
                         if let Some(sub) = self.find_sub_by_id(*id) {
                              self.mode = InputMode::Popup(PopupState::EditSub {
                                 todo_id: 0,
                                 sub_id: *id,
                                 input: sub.title.clone(),
                                 notes: sub.notes.clone().unwrap_or_default(),
                                 created_at: sub.created_at,
                                 updated_at: sub.updated_at,
                                 focus: FocusedField::Title,
                                 date_unit: 0,
                             });
                          }
                     }
                 }
            }
             keymap::Action::MarkDone => {
                if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                    if *depth == 0 {
                         ops::toggle_done(&self.store, *id)?;
                    } else if let Some(sub) = self.find_sub_by_id(*id) {
                         ops::edit_subtask(&self.store, 0, *id, None, Some(!sub.done), None, None, None)?;
                    }
                    self.reload()?;
                }
            }
            keymap::Action::CompleteTask => {
                if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                    if *depth == 0 {
                        ops::move_todo(&self.store, *id, Category::Completed)?;
                        self.reload()?;
                    } else {
                        self.mode = InputMode::Popup(PopupState::Message { 
                            text: "The 'C' command only works for top-level tasks. \nSubtasks should be marked done with 'space' or 'x'.".to_string() 
                        });
                    }
                }
            }
            keymap::Action::ToggleNotes => {
                self.show_notes = !self.show_notes;
            }
             keymap::Action::UndoDone => {
                if let Some(RowRef::Item { id, category, .. }) = self.rows.get(self.cursor) {
                    if *category == Category::Completed {
                        ops::move_todo(&self.store, *id, Category::Short)?;
                        self.reload()?;
                    }
                }
            }
            keymap::Action::Delete => {
                if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                     self.mode = InputMode::Popup(PopupState::DeleteConfirm { id: *id, is_sub: *depth > 0 });
                }
            }
            keymap::Action::Color => {
                // ...
            }
            keymap::Action::SelectColor(idx) => {
                 let tag = match idx {
                     1 => ColorTag::Red,
                     2 => ColorTag::Yellow,
                     3 => ColorTag::Green,
                     4 => ColorTag::Blue,
                     5 => ColorTag::Magenta,
                     6 => ColorTag::Cyan,
                     7 => ColorTag::Gray,
                     _ => ColorTag::None,
                 };
                 if let Some(RowRef::Item { id, .. }) = self.rows.get(self.cursor) {
                     ops::set_color(&self.store, *id, tag)?;
                     self.reload()?;
                 }
            }
            keymap::Action::Move => {
                if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                    if *depth == 0 {
                        self.mode = InputMode::Popup(PopupState::Move { id: *id, current_selection: 0 });
                    }
                }
            }
            keymap::Action::NewSubtask => {
                if let Some(RowRef::Item { id, .. }) = self.rows.get(self.cursor) {
                    let now = Local::now().timestamp();
                    self.mode = InputMode::Popup(PopupState::AddSub { 
                        todo_id: *id, 
                        input: String::new(),
                        notes: String::new(),
                        created_at: now,
                        updated_at: now,
                        focus: FocusedField::Title,
                        date_unit: 0,
                    });
                }
            }
            keymap::Action::ToggleSubtask => {
                if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
                    if *depth > 0 {
                         if let Some(sub) = self.find_sub_by_id(*id) {
                             ops::edit_subtask(&self.store, 0, *id, None, Some(!sub.done), None, None, None)?;
                             self.reload()?;
                         }
                    }
                }
            }
            _ => {}
        }
        Ok(())
    }

    fn handle_popup(&mut self, key: event::KeyEvent) -> anyhow::Result<()> {
        match key.code {
            event::KeyCode::Esc => {
                self.mode = InputMode::Normal;
            }
            event::KeyCode::Enter => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { category, input, notes, created_at, updated_at, .. } => {
                            ops::add_todo(&self.store, *category, input.clone(), if notes.is_empty() { None } else { Some(notes.clone()) }, Some(*created_at), Some(*updated_at))?;
                        },
                        PopupState::EditTodo { id, input, notes, created_at, updated_at, .. } => {
                             ops::edit_todo(&self.store, *id, Some(input.clone()), Some(notes.clone()), Some(*created_at), Some(*updated_at))?;
                        },
                         PopupState::AddSub { todo_id, input, notes, created_at, updated_at, .. } => {
                             ops::add_subtask(&self.store, *todo_id, input.clone(), if notes.is_empty() { None } else { Some(notes.clone()) }, Some(*created_at), Some(*updated_at))?;
                          },
                          PopupState::EditSub { sub_id, input, notes, created_at, updated_at, .. } => {
                              ops::edit_subtask(&self.store, 0, *sub_id, Some(input.clone()), None, Some(notes.clone()), Some(*created_at), Some(*updated_at))?;
                          },
                         PopupState::DeleteConfirm { id, is_sub } => {
                             if *is_sub {
                                 ops::remove_subtask(&self.store, 0, *id)?;
                             } else {
                                 ops::remove_todo(&self.store, *id)?;
                             }
                         },
                         PopupState::Move { id, current_selection } => {
                            let cats = State::all_categories();
                            if let Some(cat) = cats.get(*current_selection) {
                                 ops::move_todo(&self.store, *id, *cat)?;
                            }
                        },
                        PopupState::Message { .. } => {}
                    }
                }
                self.reload()?;
                self.mode = InputMode::Normal;
            }
            event::KeyCode::Tab => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { focus, .. } | PopupState::EditTodo { focus, .. } |
                        PopupState::AddSub { focus, .. } | PopupState::EditSub { focus, .. } => {
                            *focus = match focus {
                                FocusedField::Title => FocusedField::Notes,
                                FocusedField::Notes => FocusedField::Created,
                                FocusedField::Created => FocusedField::Updated,
                                FocusedField::Updated => FocusedField::Title,
                            };
                        }
                        _ => {}
                    }
                }
            }
            event::KeyCode::Left => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { focus, date_unit, .. } | PopupState::EditTodo { focus, date_unit, .. } |
                        PopupState::AddSub { focus, date_unit, .. } | PopupState::EditSub { focus, date_unit, .. } => {
                            if matches!(focus, FocusedField::Created | FocusedField::Updated) {
                                *date_unit = date_unit.checked_sub(1).unwrap_or(5);
                            }
                        }
                        _ => {}
                    }
                }
            }
            event::KeyCode::Right => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { focus, date_unit, .. } | PopupState::EditTodo { focus, date_unit, .. } |
                        PopupState::AddSub { focus, date_unit, .. } | PopupState::EditSub { focus, date_unit, .. } => {
                            if matches!(focus, FocusedField::Created | FocusedField::Updated) {
                                *date_unit = (*date_unit + 1) % 6;
                            }
                        }
                        _ => {}
                    }
                }
            }
            event::KeyCode::Up => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { focus, date_unit, created_at, updated_at, .. } | 
                        PopupState::EditTodo { focus, date_unit, created_at, updated_at, .. } |
                        PopupState::AddSub { focus, date_unit, created_at, updated_at, .. } | 
                        PopupState::EditSub { focus, date_unit, created_at, updated_at, .. } => {
                            match focus {
                                FocusedField::Created => *created_at = adjust_timestamp(*created_at, *date_unit, 1),
                                FocusedField::Updated => *updated_at = adjust_timestamp(*updated_at, *date_unit, 1),
                                _ => {}
                            }
                        }
                         PopupState::Move { current_selection, .. } => {
                            *current_selection = current_selection.saturating_sub(1);
                        }
                        _ => {}
                    }
                }
            }
            event::KeyCode::Down => {
                if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { focus, date_unit, created_at, updated_at, .. } | 
                        PopupState::EditTodo { focus, date_unit, created_at, updated_at, .. } |
                        PopupState::AddSub { focus, date_unit, created_at, updated_at, .. } | 
                        PopupState::EditSub { focus, date_unit, created_at, updated_at, .. } => {
                            match focus {
                                FocusedField::Created => *created_at = adjust_timestamp(*created_at, *date_unit, -1),
                                FocusedField::Updated => *updated_at = adjust_timestamp(*updated_at, *date_unit, -1),
                                _ => {}
                            }
                        }
                         PopupState::Move { current_selection, .. } => {
                            *current_selection = (*current_selection + 1).min(3);
                        }
                        _ => {}
                    }
                }
            }
            event::KeyCode::Char(c) => {
                 if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { input, notes, focus, .. } | 
                        PopupState::EditTodo { input, notes, focus, .. } |
                        PopupState::AddSub { input, notes, focus, .. } | 
                        PopupState::EditSub { input, notes, focus, .. } => {
                            match focus {
                                FocusedField::Title => input.push(c),
                                FocusedField::Notes => notes.push(c),
                                _ => {}
                            }
                        },
                        _ => {}
                    }
                 }
            }
            event::KeyCode::Backspace => {
                 if let InputMode::Popup(state) = &mut self.mode {
                    match state {
                        PopupState::AddTodo { input, notes, focus, .. } | 
                        PopupState::EditTodo { input, notes, focus, .. } |
                        PopupState::AddSub { input, notes, focus, .. } | 
                        PopupState::EditSub { input, notes, focus, .. } => {
                            match focus {
                                FocusedField::Title => { input.pop(); }
                                FocusedField::Notes => { notes.pop(); }
                                _ => {}
                            }
                        },
                        _ => {}
                    }
                 }
            }
            _ => {}
        }
        Ok(())
    }

    fn reload(&mut self) -> anyhow::Result<()> {
        self.state = self.store.load()?;
        self.rebuild_rows();
        Ok(())
    }

    fn get_current_category(&self) -> Option<Category> {
        if let Some(row) = self.rows.get(self.cursor) {
            match row {
                RowRef::Header(cat) => Some(*cat),
                RowRef::Item { category, .. } => Some(*category),
            }
        } else {
            None
        }
    }

    pub fn get_current_item_notes(&self) -> Option<String> {
        if let Some(RowRef::Item { id, depth, .. }) = self.rows.get(self.cursor) {
            if *depth == 0 {
                self.find_todo(*id).and_then(|t| t.notes.clone())
            } else {
                self.find_sub_by_id(*id).and_then(|s| s.notes.clone())
            }
        } else {
            None
        }
    }

    fn find_todo(&self, id: u64) -> Option<&crate::core::model::Todo> {
        for cat in State::all_categories() {
            if let Some(t) = self.state.get_category(cat).iter().find(|t| t.id == id) {
                return Some(t);
            }
        }
        None
    }

    fn find_sub_by_id(&self, sub_id: u64) -> Option<&crate::core::model::SubTask> {
         for cat in State::all_categories() {
             for todo in self.state.get_category(cat) {
                 if let Some(sub) = find_sub_recursive(&todo.subtasks, sub_id) {
                     return Some(sub);
                 }
             }
         }
         None
    }

}

fn find_sub_recursive(subs: &[crate::core::model::SubTask], id: u64) -> Option<&crate::core::model::SubTask> {
    for sub in subs {
        if sub.id == id { return Some(sub); }
        if let Some(found) = find_sub_recursive(&sub.subtasks, id) {
            return Some(found);
        }
    }
    None
}

fn adjust_timestamp(ts: i64, unit: usize, delta: i64) -> i64 {
    use chrono::{Datelike, Timelike, TimeZone};
    let dt = Local.timestamp_opt(ts, 0).unwrap();
    let (mut y, mut m, d, mut h, min, s) = (dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second());
    
    match unit {
        0 => y += delta as i32,
        1 => {
            let mut nm = m as i32 + delta as i32;
            while nm > 12 { nm -= 12; y += 1; }
            while nm < 1 { nm += 12; y -= 1; }
            m = nm as u32;
        }
        2 => {
            let next = dt + chrono::Duration::days(delta);
            return next.timestamp();
        }
        3 => {
            let mut nh = h as i32 + delta as i32;
            while nh >= 24 { nh -= 24; }
            while nh < 0 { nh += 24; }
            h = nh as u32;
        }
        4 => {
            let next = dt + chrono::Duration::minutes(delta);
            return next.timestamp();
        }
        5 => {
            let next = dt + chrono::Duration::seconds(delta);
            return next.timestamp();
        }
        _ => {}
    }
    
    Local.with_ymd_and_hms(y, m, d, h, min, s).single()
        .unwrap_or(dt) // Fallback for invalid dates
        .timestamp()
}
