use dittolive_ditto::transport::PresenceOs;
use ratatui::{
    backend::Backend,
    layout::Alignment,
    prelude::*,
    style::{Color, Style},
    widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Tabs},
    Frame,
};

use crate::app::App;

/// Renders the user interface widgets.
pub fn render<B: Backend>(app: &mut App, frame: &mut Frame<'_, B>) {
    // This is where you add new widgets.
    // See the following resources:
    // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
    // - https://github.com/ratatui-org/ratatui/tree/master/examples
    let chunks = Layout::default()
        .constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
        .split(frame.size());

    let header_chunks = Layout::default()
        .constraints([Constraint::Length(30), Constraint::Min(0)].as_ref())
        .direction(Direction::Horizontal)
        .split(chunks[0]);

    let titles = app
        .tabs
        .titles
        .iter()
        .map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
        .collect();
    let tabs = Tabs::new(titles)
        .block(Block::default().borders(Borders::ALL).title(App::title()))
        .highlight_style(Style::default().bg(Color::DarkGray))
        .select(app.tabs.index);
    frame.render_widget(tabs, header_chunks[0]);

    let status_content = app.status_header();
    let status = Paragraph::new(status_content)
        .style(Style::default().add_modifier(Modifier::BOLD))
        .alignment(Alignment::Right)
        .block(Block::default().borders(Borders::ALL));

    frame.render_widget(status, header_chunks[1]);

    match app.tabs.index {
        0 => render_cars(app, frame, chunks[1]),
        _ => render_peers(app, frame, chunks[1]),
    }
}

fn render_cars<B: Backend>(app: &mut App, frame: &mut Frame<'_, B>, area: Rect) {
    // -------- Car Header -----------
    let chunks = Layout::default()
        .constraints([Constraint::Length(2), Constraint::Min(0)].as_ref())
        .split(area);
    frame.render_widget(
        Paragraph::new(crate::car::Car::header())
            .style(
                Style::default()
                    .add_modifier(Modifier::BOLD)
                    .bg(Color::DarkGray),
            )
            .block(
                Block::default()
                    .borders(Borders::from_bits(11).unwrap())
                    .bg(Color::Black),
            ),
        chunks[0],
    );

    // -------- Car list -----------
    let cars = app.cars.read().unwrap();
    let cars_list: Vec<ListItem<'_>> = {
        cars.items
            .iter()
            .map(|raw_car| ListItem::new(raw_car.display()))
            .collect()
    };
    frame.render_stateful_widget(
        List::new(cars_list)
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
            .highlight_symbol(">>")
            .block(Block::default().borders(Borders::from_bits(14).unwrap())),
        chunks[1],
        &mut cars.state.clone(),
    );

    // -------- HELP BOX -----------
    let chunks = Layout::default()
        .constraints([Constraint::Length(3), Constraint::Max(3)].as_ref())
        .split(chunks[1]);

    frame.render_widget(
        Paragraph::new("`Q`uit|`A`dd car|`D`rop car|`B`ig Peer|`S`ync")
            .style(Style::default().fg(Color::Cyan).bg(Color::Black))
            .block(
                Block::default()
                    .title("Help")
                    .title_alignment(Alignment::Center)
                    .borders(Borders::ALL)
                    .border_type(BorderType::Rounded),
            ),
        chunks[1],
    )
}

fn render_peers<B: Backend>(app: &mut App, frame: &mut Frame<'_, B>, area: Rect) {
    let body_chunks = Layout::default()
        .constraints([Constraint::Length(32), Constraint::Min(0)].as_ref())
        .direction(Direction::Horizontal)
        .split(area);
    // -------- Peers Header -----------
    let peers_chunks = Layout::default()
        .constraints([Constraint::Length(2), Constraint::Min(0)].as_ref())
        .split(body_chunks[0]);

    frame.render_widget(
        Paragraph::new(format!("  |{:>16}|{:>10}| ", "Device name", "Platform"))
            .style(
                Style::default()
                    .add_modifier(Modifier::BOLD)
                    .bg(Color::DarkGray),
            )
            .block(
                Block::default()
                    .borders(Borders::from_bits(11).unwrap())
                    .bg(Color::Black),
            ),
        peers_chunks[0],
    );

    // -------- Peers ------------------
    let peers = app.peers.read().unwrap();
    let peer_list: Vec<ListItem<'_>> = {
        peers
            .items
            .iter()
            .map(|peer| {
                ListItem::new(format!(
                    "|{:>16.16}|{:>8}|",
                    peer.device_name,
                    peer.os.as_ref().unwrap_or(&PresenceOs::Generic).to_string()
                ))
            })
            .collect()
    };
    frame.render_stateful_widget(
        List::new(peer_list)
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
            .highlight_symbol(">>")
            .block(Block::default().borders(Borders::from_bits(14).unwrap())),
        peers_chunks[1],
        &mut peers.state.clone(),
    );
    // -------- Selected peer -----------
    let content = if let Some(index) = peers.state.selected() {
        let peer = peers.items.get(index).unwrap();
        format!(
            r#"
Device             {}
PeerKey            {:?}
Connected to cloud {}
Connections        {:#?}
            "#,
            peer.device_name,
            peer.peer_key_string,
            peer.is_connected_to_ditto_cloud,
            peer.connections
                .iter()
                .map(|conn| &conn.id)
                .collect::<Vec<_>>()
        )
    } else {
        String::from("No peer available.")
    };
    frame.render_widget(
        Paragraph::new(content)
            .style(Style::default().add_modifier(Modifier::BOLD))
            .block(Block::default().borders(Borders::ALL)),
        body_chunks[1],
    );
}
