use core::fmt::Write;

use embedded_cli::Command;
use embedded_cli::cli::CliBuilder;
use esp_hal::{
    Async,
    uart::{TxError, UartRx},
};
use log::{error, info};

use crate::logging::uart::with_uart_tx;

struct Writer;

impl embedded_io::ErrorType for Writer {
    type Error = TxError;
}

impl embedded_io::Write for Writer {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
        with_uart_tx(|uart| uart.write(buf))
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        with_uart_tx(|uart| uart.flush())
    }
}

#[derive(Command)]
enum Base /*<'a>*/ {
    // /// Say hello to World or someone else
    // Hello {
    //     /// To whom to say hello (World by default)
    //     name: Option<&'a str>,
    // },
    /// Display the version of the firmware.
    Version,

    /// Stop CLI and exit.
    Reset,
}

pub async fn run_console(mut uart_rx: UartRx<'_, Async>) {
    let mut buf = [0_u8; 32];
    let command_buffer = [0_u8; 32];
    let mut cli = CliBuilder::default()
        .writer(Writer)
        .command_buffer(command_buffer)
        .prompt("")
        .build()
        .unwrap();

    info!("Debugging console opened.");

    loop {
        let len = match uart_rx.read_async(&mut buf[..]).await {
            Ok(len) => len,
            Err(error) => {
                error!("Failed to read from the alt UART port: {error:?}");
                continue;
            }
        };

        for &byte in &buf[0..len] {
            cli.process_byte::<Base, _>(
                byte,
                &mut Base::processor(|cli, command| {
                    match command {
                        // Base::Hello { name } => {
                        //     write!(cli.writer(), "Hello, {}", name.unwrap_or("World"))?;
                        // }
                        Base::Version => {
                            cli.writer()
                                .write_fmt(format_args!(
                                    "{} - {} - {}",
                                    env!("CARGO_PKG_NAME"),
                                    env!("CARGO_PKG_VERSION"),
                                    env!("GIT_COMMIT")
                                ))
                                .unwrap();
                        }
                        Base::Reset => {
                            cli.writer()
                                .write_str("Performing software reset.")
                                .unwrap();
                            esp_hal::system::software_reset();
                        }
                    }
                    Ok(())
                }),
            )
            .unwrap();
        }
    }
}
