use core::panic;
use std::{collections::HashMap, io::Write};

use niri_ipc::{
    socket::Socket,
    state::{EventStreamState, EventStreamStatePart},
    Request, Response, Workspace,
};
use serde::Serialize;

// gruvbox colors
const COLORS: [[&str; 2]; 7] = [
    ["#458588", "#83a598"], // blue
    ["#b16286", "#d3869b"], // purple
    ["#689d6a", "#8ec07c"], // aqua
    ["#97971a", "#b8bb26"], // green
    ["#d79921", "#fabd2f"], // yellow
    ["#d65d0e", "#fe8019"], // orange
    ["#cc241d", "#fb4934"], // red
];

fn main() -> Result<(), std::io::Error> {
    let mut state = EventStreamState::default();
    let mut sock = Socket::connect()?;
    // let mut func = sock.send(Request::EventStream).and_then(|it| match it {
    //     (Ok(Response::Handled), func) => Ok(func),
    //     _ => unreachable!(),
    // })?;
    // let mut func = sock.send(Request::EventStream).and_then(|it| match it {
    //     Ok(Response::Handled) => {}
    //     Ok(_) => panic!("???"),
    //     Err(e) => panic!("e"),
    // });
    let r = sock.send(Request::EventStream)?;
    match r {
        Ok(Response::Handled) => {}
        e => panic!("unexpected {e:?}"),
    }

    let mut receiver = sock.read_events();
    while let Ok(ev) = receiver() {
        // check only relevant later, only done here to avoid a clone
        let ev_is_ws_related = matches!(
            &ev,
            niri_ipc::Event::WorkspacesChanged { .. }
                | niri_ipc::Event::WorkspaceActivated { .. }
                | niri_ipc::Event::WorkspaceActiveWindowChanged { .. }
        );

        // apply event to state
        state.apply(ev);

        if ev_is_ws_related {
            let mut workspaces = state
                .workspaces
                .workspaces
                .values()
                .map(|it| it.clone())
                .collect::<Vec<Workspace>>();

            workspaces.sort_by(|a, b| a.idx.cmp(&b.idx));
            workspaces.sort_by(|a, b| {
                a.output
                    .clone()
                    .expect("unreachable")
                    .to_lowercase()
                    .cmp(&b.output.clone().expect("unreachable").to_lowercase())
            });

            let output_colors_lut = workspaces
                .iter()
                .map(|it| it.output.clone().expect("unreachable"))
                .fold(Vec::new(), |mut acc, it| {
                    if !acc.contains(&it) {
                        acc.push(it);
                    }
                    acc
                })
                .into_iter()
                .enumerate()
                .map(|(idx, val)| (val, &COLORS[idx]))
                .collect::<HashMap<String, &[&str; 2]>>();

            let results = workspaces
                .iter()
                .map(
                    |Workspace {
                         idx,
                         output,
                         is_active,
                         is_focused,
                         name,
                         ..
                     }| WsData {
                        color: output_colors_lut[&output.clone().expect("unreachable")]
                            [if *is_active { 1 } else { 0 }],
                        idx: *idx,
                        focused: *is_focused,
                        active: *is_active,
                        icon: name.as_ref().map(|name| match name.as_str() {
                            "aaa social" => "󰭹",
                            "bbb browser" => "󰈹",
                            "ccc notes" => "󰎚",
                            &_ => "!",
                        }),
                    },
                )
                .collect::<Vec<_>>();

            println!("{}", serde_json::to_string(&results).unwrap());
        }
    }

    Ok(())
}

#[derive(Debug, Serialize)]
struct WsData {
    color: &'static str,
    idx: u8,
    focused: bool,
    active: bool,
    icon: Option<&'static str>,
}
