//! Topology TUI with ratatui
//!
//! Provides a 3-panel interface for exploring project topology:
//! - Left: Navigation menu (Services, Nodes, Topics)
//! - Right: Details of selected section
//! - Footer: Instructions and links

use crate::services::Topology;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::{
    layout::{Constraint, Direction, Layout},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap},
    Frame,
};

/// Navigation sections
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Section {
    Services,
    Nodes,
    Topics,
}

impl Section {
    fn all() -> Vec<Section> {
        vec![Section::Services, Section::Nodes, Section::Topics]
    }

    fn label(&self) -> &str {
        match self {
            Section::Services => "Services",
            Section::Nodes => "Nodes",
            Section::Topics => "Topics",
        }
    }

    fn icon(&self) -> &str {
        match self {
            Section::Services => "🌐",
            Section::Nodes => "🔌",
            Section::Topics => "📨",
        }
    }
}

/// TUI for topology visualization
pub struct TopologyTui {
    topology: Topology,
    current_section: Section,
    detail_scroll_offset: usize,
    should_quit: bool,
}

impl TopologyTui {
    /// Create a new topology TUI
    pub fn new(topology: Topology) -> Self {
        Self {
            topology,
            current_section: Section::Services,
            detail_scroll_offset: 0,
            should_quit: false,
        }
    }

    /// Check if should quit
    pub fn should_quit(&self) -> bool {
        self.should_quit
    }

    /// Draw the TUI (single frame)
    pub fn draw(&mut self, f: &mut Frame) {
        // Split terminal vertically: content area + footer
        let vertical_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Min(10),   // Main content area
                Constraint::Length(3), // Footer
            ])
            .split(f.area());

        // Split main content horizontally: navigation + details
        let horizontal_chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([
                Constraint::Percentage(30), // Navigation
                Constraint::Percentage(70), // Details
            ])
            .split(vertical_chunks[0]);

        // Draw panels
        self.draw_navigation(f, horizontal_chunks[0]);
        self.draw_details(f, horizontal_chunks[1]);
        self.draw_footer(f, vertical_chunks[1]);
    }

    /// Draw navigation panel
    fn draw_navigation(&mut self, f: &mut Frame, area: ratatui::layout::Rect) {
        let sections = Section::all();
        let items: Vec<ListItem> = sections
            .iter()
            .map(|section| {
                let is_selected = *section == self.current_section;

                let (prefix, style) = if is_selected {
                    ("▶ ", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD))
                } else {
                    ("  ", Style::default().fg(Color::White))
                };

                let formatted = format!("{} {} {}", prefix, section.icon(), section.label());
                ListItem::new(formatted).style(style)
            })
            .collect();

        let title = format!(" {} ", self.topology.project_name);
        let list = List::new(items).block(
            Block::default()
                .borders(Borders::ALL)
                .title(title)
                .border_style(Style::default().fg(Color::Cyan)),
        );

        // Create list state for highlighting
        let mut state = ListState::default();
        let selected_index = sections.iter().position(|s| *s == self.current_section).unwrap_or(0);
        state.select(Some(selected_index));

        f.render_stateful_widget(list, area, &mut state);
    }

    /// Draw details panel
    fn draw_details(&self, f: &mut Frame, area: ratatui::layout::Rect) {
        match self.current_section {
            Section::Services => self.draw_services_details(f, area),
            Section::Nodes => self.draw_nodes_details(f, area),
            Section::Topics => self.draw_topics_details(f, area),
        }
    }

    /// Draw Services details
    fn draw_services_details(&self, f: &mut Frame, area: ratatui::layout::Rect) {
        let mut text = vec![
            Line::from(vec![Span::styled(
                "🌐 SERVICES",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if self.topology.services.is_empty() {
            text.push(Line::from(vec![Span::styled(
                "No services configured.",
                Style::default().fg(Color::DarkGray),
            )]));
            text.push(Line::from(""));
            text.push(Line::from("Services can include:"));
            text.push(Line::from("  • HTTP API servers"));
            text.push(Line::from("  • Database connections"));
            text.push(Line::from("  • External integrations"));
        } else {
            for service in &self.topology.services {
                text.push(Line::from(vec![Span::styled(
                    &service.name,
                    Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
                )]));
                text.push(Line::from(vec![
                    Span::raw("  Host: "),
                    Span::styled(&service.host, Style::default().fg(Color::Green)),
                ]));
                text.push(Line::from(vec![
                    Span::raw("  Port: "),
                    Span::styled(service.port.to_string(), Style::default().fg(Color::Green)),
                ]));
                text.push(Line::from(vec![
                    Span::raw("  URL:  "),
                    Span::styled(
                        format!("http://{}:{}", service.host, service.port),
                        Style::default().fg(Color::Yellow),
                    ),
                ]));
                text.push(Line::from(""));
            }
        }

        let paragraph = Paragraph::new(text)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(format!(" Services ({}) ", self.topology.services.len()))
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: true });

        f.render_widget(paragraph, area);
    }

    /// Draw Nodes details
    fn draw_nodes_details(&self, f: &mut Frame, area: ratatui::layout::Rect) {
        let mut text = vec![
            Line::from(vec![Span::styled(
                "🔌 NODES & TOPICS",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if self.topology.nodes.is_empty() {
            text.push(Line::from(vec![Span::styled(
                "No nodes found.",
                Style::default().fg(Color::DarkGray),
            )]));
        } else {
            for node in &self.topology.nodes {
                text.push(Line::from(vec![
                    Span::raw("📦 "),
                    Span::styled(
                        &node.name,
                        Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
                    ),
                ]));

                text.push(Line::from(vec![
                    Span::raw("   Package: "),
                    Span::styled(&node.package, Style::default().fg(Color::Yellow)),
                ]));

                if let Some(desc) = &node.description {
                    text.push(Line::from(vec![
                        Span::raw("   "),
                        Span::styled(desc, Style::default().fg(Color::DarkGray)),
                    ]));
                }

                // Publishes
                if !node.publishes.is_empty() {
                    text.push(Line::from(vec![Span::styled(
                        "   ├─ Publishes:",
                        Style::default().fg(Color::DarkGray),
                    )]));
                    for topic in &node.publishes {
                        let msg_type = topic
                            .message_type
                            .as_ref()
                            .map(|t| format!(" ({})", t))
                            .unwrap_or_default();
                        text.push(Line::from(vec![
                            Span::raw("   │  ├─ "),
                            Span::styled(&topic.path, Style::default().fg(Color::Green)),
                            Span::styled(msg_type, Style::default().fg(Color::Yellow)),
                        ]));
                    }
                } else {
                    text.push(Line::from(vec![Span::styled(
                        "   ├─ Publishes: (none)",
                        Style::default().fg(Color::DarkGray),
                    )]));
                }

                // Subscribes
                if !node.subscribes.is_empty() {
                    text.push(Line::from(vec![Span::styled(
                        "   └─ Subscribes:",
                        Style::default().fg(Color::DarkGray),
                    )]));
                    for topic in &node.subscribes {
                        let msg_type = topic
                            .message_type
                            .as_ref()
                            .map(|t| format!(" ({})", t))
                            .unwrap_or_default();
                        text.push(Line::from(vec![
                            Span::raw("      ├─ "),
                            Span::styled(&topic.path, Style::default().fg(Color::Blue)),
                            Span::styled(msg_type, Style::default().fg(Color::Yellow)),
                        ]));
                    }
                } else {
                    text.push(Line::from(vec![Span::styled(
                        "   └─ Subscribes: (none)",
                        Style::default().fg(Color::DarkGray),
                    )]));
                }

                text.push(Line::from(""));
            }
        }

        // Skip lines for scroll offset
        let text: Vec<Line> = text.into_iter().skip(self.detail_scroll_offset).collect();

        let paragraph = Paragraph::new(text)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(format!(" Nodes ({}) ", self.topology.nodes.len()))
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: true });

        f.render_widget(paragraph, area);
    }

    /// Draw Topics details
    fn draw_topics_details(&self, f: &mut Frame, area: ratatui::layout::Rect) {
        let mut text = vec![
            Line::from(vec![Span::styled(
                "📨 TOPICS",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if self.topology.topics.is_empty() {
            text.push(Line::from(vec![Span::styled(
                "No topics found.",
                Style::default().fg(Color::DarkGray),
            )]));
        } else {
            for topic in &self.topology.topics {
                let msg_type = topic
                    .message_type
                    .as_ref()
                    .map(|t| format!(" ({})", t))
                    .unwrap_or_default();

                text.push(Line::from(vec![
                    Span::raw("📨 "),
                    Span::styled(
                        &topic.path,
                        Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
                    ),
                    Span::styled(msg_type, Style::default().fg(Color::Yellow)),
                ]));

                // Publishers
                if !topic.publishers.is_empty() {
                    text.push(Line::from(vec![Span::styled(
                        "   ├─ Publishers:",
                        Style::default().fg(Color::DarkGray),
                    )]));
                    for node in &topic.publishers {
                        text.push(Line::from(vec![
                            Span::raw("   │  ├─ "),
                            Span::styled(node, Style::default().fg(Color::Green)),
                        ]));
                    }
                } else {
                    text.push(Line::from(vec![Span::styled(
                        "   ├─ Publishers: (none)",
                        Style::default().fg(Color::DarkGray),
                    )]));
                }

                // Subscribers
                if !topic.subscribers.is_empty() {
                    text.push(Line::from(vec![Span::styled(
                        "   └─ Subscribers:",
                        Style::default().fg(Color::DarkGray),
                    )]));
                    for node in &topic.subscribers {
                        text.push(Line::from(vec![
                            Span::raw("      ├─ "),
                            Span::styled(node, Style::default().fg(Color::Blue)),
                        ]));
                    }
                } else {
                    text.push(Line::from(vec![Span::styled(
                        "   └─ Subscribers: (none)",
                        Style::default().fg(Color::DarkGray),
                    )]));
                }

                text.push(Line::from(""));
            }
        }

        // Skip lines for scroll offset
        let text: Vec<Line> = text.into_iter().skip(self.detail_scroll_offset).collect();

        let paragraph = Paragraph::new(text)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(format!(" Topics ({}) ", self.topology.topics.len()))
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: true });

        f.render_widget(paragraph, area);
    }

    /// Draw footer with links
    fn draw_footer(&self, f: &mut Frame, area: ratatui::layout::Rect) {
        let footer_text = vec![Line::from(vec![
            Span::raw("  "),
            Span::styled("↑/↓:", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
            Span::raw(" Navigate  "),
            Span::styled("j/k:", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
            Span::raw(" Scroll  "),
            Span::styled("Q/ESC:", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)),
            Span::raw(" Exit"),
        ])];

        let footer = Paragraph::new(footer_text).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(Color::DarkGray)),
        );

        f.render_widget(footer, area);
    }

    /// Handle keyboard input
    pub fn handle_key(&mut self, key: KeyEvent) {
        match key.code {
            // Exit
            KeyCode::Char('q') | KeyCode::Esc => {
                self.should_quit = true;
            }
            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                self.should_quit = true;
            }

            // Navigate sections
            KeyCode::Up => {
                let sections = Section::all();
                let current_index = sections.iter().position(|s| *s == self.current_section).unwrap_or(0);
                if current_index > 0 {
                    self.current_section = sections[current_index - 1];
                    self.detail_scroll_offset = 0; // Reset scroll when changing sections
                }
            }
            KeyCode::Down => {
                let sections = Section::all();
                let current_index = sections.iter().position(|s| *s == self.current_section).unwrap_or(0);
                if current_index < sections.len() - 1 {
                    self.current_section = sections[current_index + 1];
                    self.detail_scroll_offset = 0; // Reset scroll when changing sections
                }
            }

            // Scroll details (PgUp/PgDn, Ctrl+U/D, or j/k for laptops)
            KeyCode::PageUp | KeyCode::Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_sub(10);
            }
            KeyCode::PageDown | KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_add(10);
            }
            KeyCode::Char('k') => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_sub(1);
            }
            KeyCode::Char('j') => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_add(1);
            }

            _ => {}
        }
    }
}
