use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
    Frame,
};
use std::collections::HashMap;

use crate::commands::scaffolds::{ScaffoldRegistry, ScaffoldArgs, ScaffoldResult, FieldSpec};
use crate::tui::app::AppAction;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Focus {
    ScaffoldList,
    NameInput,
    ModelInput,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Screen {
    List,
    Result,
}

pub struct CodeGenTab {
    registry: ScaffoldRegistry,
    scaffold_names: Vec<&'static str>,
    scaffold_descs: Vec<&'static str>,
    list_state: ListState,
    filter: String,
    filter_active: bool,
    focus: Focus,
    input_name: String,
    input_model: String,
    result: Option<ScaffoldResult>,
    screen: Screen,
    status_message: String,
    status_timer: u8,
}

impl Default for CodeGenTab {
    fn default() -> Self {
        let registry = ScaffoldRegistry::new();
        let scaffold_list = registry.list();
        let names: Vec<&'static str> = scaffold_list.iter().map(|(n, _)| *n).collect();
        let descs: Vec<&'static str> = scaffold_list.iter().map(|(_, d)| *d).collect();
        let mut state = ListState::default();
        state.select(Some(0));
        Self {
            registry,
            scaffold_names: names,
            scaffold_descs: descs,
            list_state: state,
            filter: String::new(),
            filter_active: false,
            focus: Focus::ScaffoldList,
            input_name: String::new(),
            input_model: String::new(),
            result: None,
            screen: Screen::List,
            status_message: String::new(),
            status_timer: 0,
        }
    }
}

impl CodeGenTab {
    pub async fn load(&mut self, _pool: &sqlx::PgPool) {
    }

    fn filtered_indices(&self) -> Vec<usize> {
        if self.filter.is_empty() {
            (0..self.scaffold_names.len()).collect()
        } else {
            let f = self.filter.to_lowercase();
            self.scaffold_names
                .iter()
                .enumerate()
                .filter(|(_, n)| n.to_lowercase().contains(&f))
                .map(|(i, _)| i)
                .collect()
        }
    }

    fn selected_scaffold(&self) -> Option<&'static str> {
        self.list_state.selected().and_then(|sel| {
            let indices = self.filtered_indices();
            indices.get(sel).copied().map(|i| self.scaffold_names[i])
        })
    }

    fn execute_generate(&mut self) {
        let name = self.selected_scaffold();
        let scaffold = match name.and_then(|n| self.registry.get(n)) {
            Some(s) => s,
            None => {
                self.status_message = "No scaffold selected".into();
                self.status_timer = 5;
                return;
            }
        };

        let flags = HashMap::new();
        let mut fields: Vec<FieldSpec> = Vec::new();
        if !self.input_fields_raw().is_empty() {
            for part in self.input_fields_raw().split(',') {
                let part = part.trim();
                if part.is_empty() { continue; }
                let (fname, ftype) = if let Some(idx) = part.find(':') {
                    (&part[..idx], &part[idx+1..])
                } else {
                    (part, "string")
                };
                fields.push(FieldSpec {
                    name: fname.to_string(),
                    r#type: ftype.to_string(),
                    validations: vec![],
                });
            }
        }

        let args = ScaffoldArgs {
            name: if self.input_name.is_empty() { None } else { Some(self.input_name.clone()) },
            model: if self.input_model.is_empty() { None } else { Some(self.input_model.clone()) },
            fields,
            flags,
            dry_run: false,
            json: false,
        };

        match scaffold.generate(&args) {
            Ok(result) => {
                self.result = Some(result);
                self.screen = Screen::Result;
                self.status_message = "Scaffold generated successfully".into();
                self.status_timer = 5;
            }
            Err(e) => {
                self.status_message = format!("Error: {e}");
                self.status_timer = 8;
            }
        }
    }

    fn input_fields_raw(&self) -> &str {
        ""
    }

    fn clear(&mut self) {
        self.input_name.clear();
        self.input_model.clear();
        self.result = None;
        self.screen = Screen::List;
        self.status_message.clear();
    }

    pub fn render(&mut self, frame: &mut Frame, area: Rect) {
        match self.screen {
            Screen::List => self.render_list(frame, area),
            Screen::Result => self.render_result(frame, area),
        }
    }

    fn render_list(&mut self, frame: &mut Frame, area: Rect) {
        let chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Ratio(2, 5), Constraint::Ratio(3, 5)])
            .split(area);

        self.render_scaffold_list(frame, chunks[0]);
        self.render_detail(frame, chunks[1]);
    }

    fn render_scaffold_list(&mut self, frame: &mut Frame, area: Rect) {
        let indices = self.filtered_indices();
        let items: Vec<ListItem> = indices
            .iter()
            .map(|i| {
                let name = self.scaffold_names[*i];
                let desc = self.scaffold_descs[*i];
                ListItem::new(Line::from(vec![
                    Span::styled(
                        format!(" {:20}", name),
                        Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
                    ),
                    Span::raw(" "),
                    Span::styled(desc, Style::default().fg(Color::White)),
                ]))
            })
            .collect();

        let title = if self.filter_active {
            format!(" Scaffolds (filter: {}) ", self.filter)
        } else {
            " Scaffolds [/ to filter] ".into()
        };

        let list = List::new(items)
            .block(Block::default().borders(Borders::ALL).title(title.as_str()))
            .highlight_style(
                Style::default()
                    .bg(if self.focus == Focus::ScaffoldList {
                        Color::DarkGray
                    } else {
                        Color::Reset
                    })
                    .add_modifier(Modifier::BOLD),
            )
            .highlight_symbol("▶ ");

        let mut state = self.list_state.clone();
        frame.render_stateful_widget(list, area, &mut state);
    }

    fn render_detail(&mut self, frame: &mut Frame, area: Rect) {
        let inner = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(6),
                Constraint::Min(3),
                Constraint::Length(4),
            ])
            .split(area);

        let input_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(3), Constraint::Length(3)])
            .margin(1)
            .split(inner[0]);

        let name_style = if self.focus == Focus::NameInput {
            Style::default().fg(Color::Yellow)
        } else {
            Style::default().fg(Color::White)
        };
        let model_style = if self.focus == Focus::ModelInput {
            Style::default().fg(Color::Yellow)
        } else {
            Style::default().fg(Color::White)
        };

        let name_input = Paragraph::new(if self.input_name.is_empty() {
            " (optional) scaffold name"
        } else {
            self.input_name.as_str()
        })
        .style(name_style)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(" Name "),
        );
        let model_input = Paragraph::new(if self.input_model.is_empty() {
            " (optional) model name"
        } else {
            self.input_model.as_str()
        })
        .style(model_style)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(" Model "),
        );

        frame.render_widget(name_input, input_chunks[0]);
        frame.render_widget(model_input, input_chunks[1]);

        let selected = self.selected_scaffold();
        let desc_text = selected
            .and_then(|n| {
                self.scaffold_names
                    .iter()
                    .position(|s| *s == n)
                    .map(|i| self.scaffold_descs[i])
            })
            .unwrap_or("Select a scaffold from the list");

        let help_lines = vec![
            Line::from(Span::raw("")),
            Line::from(vec![
                Span::styled("Description: ", Style::default().fg(Color::Cyan)),
                Span::raw(desc_text),
            ]),
            Line::from(Span::raw("")),
            Line::from(vec![
                Span::styled("Tips: ", Style::default().fg(Color::Cyan)),
                Span::raw("fields format: "),
                Span::styled("title:string,body:text,published:bool", Style::default().fg(Color::Green)),
            ]),
        ];
        let desc = Paragraph::new(help_lines)
            .block(Block::default().borders(Borders::ALL).title(" Info "))
            .style(Style::default().fg(Color::White));
        frame.render_widget(desc, inner[1]);

        let actions = Paragraph::new(Line::from(vec![
            Span::styled(" Enter ", Style::default().fg(Color::Green).bg(Color::Reset)),
            Span::raw(" Generate  "),
            Span::styled(" Tab ", Style::default().fg(Color::Cyan)),
            Span::raw(" Switch focus  "),
            Span::styled(" / ", Style::default().fg(Color::Cyan)),
            Span::raw(" Filter  "),
            Span::styled(" Esc ", Style::default().fg(Color::Red)),
            Span::raw(" Clear  "),
        ]))
        .block(Block::default().borders(Borders::ALL).title(" Actions "));
        frame.render_widget(actions, inner[2]);
    }

    fn render_result(&mut self, frame: &mut Frame, area: Rect) {
        let result = match &self.result {
            Some(r) => r,
            None => {
                self.screen = Screen::List;
                return;
            }
        };

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Min(3), Constraint::Length(4)])
            .split(area);

        let mut lines: Vec<Line> = Vec::new();
        lines.push(Line::from(vec![
            Span::styled(" Files Created ", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)),
        ]));
        lines.push(Line::from(Span::raw("")));

        for f in &result.files_created {
            let style = if f.ends_with('/') {
                Style::default().fg(Color::Cyan)
            } else {
                Style::default().fg(Color::White)
            };
            lines.push(Line::from(Span::styled(format!("  {f}"), style)));
        }

        if !result.dirs_created.is_empty() {
            lines.push(Line::from(Span::raw("")));
            lines.push(Line::from(vec![
                Span::styled(" Directories Created ", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
            ]));
            lines.push(Line::from(Span::raw("")));
            for d in &result.dirs_created {
                lines.push(Line::from(Span::styled(format!("  {d}/"), Style::default().fg(Color::Cyan))));
            }
        }

        if !result.warnings.is_empty() {
            lines.push(Line::from(Span::raw("")));
            lines.push(Line::from(vec![
                Span::styled(" Warnings ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
            ]));
            lines.push(Line::from(Span::raw("")));
            for w in &result.warnings {
                lines.push(Line::from(Span::styled(format!("  ⚠ {w}"), Style::default().fg(Color::Yellow))));
            }
        }

        let list = ratatui::widgets::List::new(lines)
            .block(Block::default().borders(Borders::ALL).title(" Generation Result "));
        frame.render_widget(list, chunks[0]);

        let actions = Paragraph::new(Line::from(vec![
            Span::styled(" Enter ", Style::default().fg(Color::Green)),
            Span::raw(" Generate again  "),
            Span::styled(" Esc ", Style::default().fg(Color::Red)),
            Span::raw(" Back to list  "),
        ]))
        .block(Block::default().borders(Borders::ALL).title(" Actions "));
        frame.render_widget(actions, chunks[1]);
    }

    pub fn handle_key(&mut self, key: crossterm::event::KeyEvent) -> AppAction {
        match self.screen {
            Screen::List => self.handle_list_key(key),
            Screen::Result => self.handle_result_key(key),
        }
    }

    fn handle_list_key(&mut self, key: crossterm::event::KeyEvent) -> AppAction {
        use crossterm::event::{KeyCode, KeyModifiers};

        if self.filter_active {
            match key.code {
                KeyCode::Esc => {
                    self.filter_active = false;
                    self.filter.clear();
                }
                KeyCode::Enter => {
                    self.filter_active = false;
                }
                KeyCode::Char(c) => {
                    self.filter.push(c);
                }
                KeyCode::Backspace => {
                    self.filter.pop();
                }
                _ => {}
            }
            return AppAction::None;
        }

        match key.code {
            KeyCode::Up => {
                let i = self.list_state.selected().unwrap_or(0).saturating_sub(1);
                self.list_state.select(Some(i));
            }
            KeyCode::Down => {
                let indices = self.filtered_indices();
                let max = indices.len().saturating_sub(1);
                let i = (self.list_state.selected().unwrap_or(0) + 1).min(max);
                self.list_state.select(Some(i));
            }
            KeyCode::Enter => {
                match self.focus {
                    Focus::ScaffoldList => {
                        if self.selected_scaffold().is_some() {
                            self.execute_generate();
                        }
                    }
                    Focus::NameInput | Focus::ModelInput => {
                        if self.selected_scaffold().is_some() {
                            self.execute_generate();
                        }
                    }
                }
            }
            KeyCode::Tab => {
                self.focus = match self.focus {
                    Focus::ScaffoldList => Focus::NameInput,
                    Focus::NameInput => Focus::ModelInput,
                    Focus::ModelInput => Focus::ScaffoldList,
                };
            }
            KeyCode::Char('/') => {
                self.filter_active = true;
                self.filter.clear();
            }
            KeyCode::Char(c) if self.focus == Focus::NameInput => {
                if !key.modifiers.contains(KeyModifiers::CONTROL) {
                    self.input_name.push(c);
                }
            }
            KeyCode::Backspace if self.focus == Focus::NameInput => {
                self.input_name.pop();
            }
            KeyCode::Char(c) if self.focus == Focus::ModelInput => {
                if !key.modifiers.contains(KeyModifiers::CONTROL) {
                    self.input_model.push(c);
                }
            }
            KeyCode::Backspace if self.focus == Focus::ModelInput => {
                self.input_model.pop();
            }
            KeyCode::Esc => {
                self.clear();
            }
            KeyCode::F(5) => {
                self.execute_generate();
            }
            _ => {}
        }
        AppAction::None
    }

    fn handle_result_key(&mut self, key: crossterm::event::KeyEvent) -> AppAction {
        use crossterm::event::KeyCode;

        match key.code {
            KeyCode::Esc => {
                self.screen = Screen::List;
            }
            KeyCode::Enter => {
                self.screen = Screen::List;
            }
            _ => {}
        }
        AppAction::None
    }
}
