use crate::bus::ChatMessage;

use super::channel::CliChannel;

pub enum InputEvent {
    Message(ChatMessage),
    Command(InputCommand),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InputCommand {
    Exit,
    New(Option<String>),
    Save(Option<String>),
    Sessions,
    Use(String),
}

pub struct InputHandler {
    channel: CliChannel,
}

impl InputHandler {
    pub fn new() -> Self {
        Self {
            channel: CliChannel::new(),
        }
    }

    pub async fn read_input(&mut self, prompt: &str) -> Result<Option<InputEvent>, InputError> {
        match self.channel.read_line(prompt).await {
            Ok(Some(line)) => {
                if line.trim().is_empty() {
                    return Ok(None);
                }

                if let Some(cmd) = self.handle_special_commands(&line) {
                    return Ok(Some(InputEvent::Command(cmd)));
                }

                Ok(Some(InputEvent::Message(ChatMessage::user(line))))
            }
            Ok(None) => Ok(None),
            Err(e) => Err(InputError::IoError(e)),
        }
    }

    pub async fn write_output(&mut self, content: &str) -> Result<(), InputError> {
        self.channel
            .write_line(content)
            .await
            .map_err(InputError::IoError)
    }

    pub async fn write_response(&mut self, content: &str) -> Result<(), InputError> {
        self.channel
            .write_response(content)
            .await
            .map_err(InputError::IoError)
    }

    fn handle_special_commands(&self, line: &str) -> Option<InputCommand> {
        let trimmed = line.trim();
        let mut parts = trimmed.splitn(2, char::is_whitespace);
        let command = parts.next()?;
        let arg = parts
            .next()
            .map(str::trim)
            .filter(|value| !value.is_empty());

        match command {
            "/quit" | "/exit" | "/q" => Some(InputCommand::Exit),
            "/new" => Some(InputCommand::New(arg.map(ToOwned::to_owned))),
            "/save" => Some(InputCommand::Save(arg.map(ToOwned::to_owned))),
            "/sessions" | "/list" => Some(InputCommand::Sessions),
            "/use" => arg.map(|value| InputCommand::Use(value.to_string())),
            _ => None,
        }
    }
}

impl Default for InputHandler {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug)]
pub enum InputError {
    IoError(std::io::Error),
}

impl std::fmt::Display for InputError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InputError::IoError(e) => write!(f, "IO error: {}", e),
        }
    }
}

impl std::error::Error for InputError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_special_command_parsing() {
        let handler = InputHandler::new();

        assert_eq!(
            handler.handle_special_commands("/quit"),
            Some(InputCommand::Exit)
        );
        assert_eq!(
            handler.handle_special_commands("/new"),
            Some(InputCommand::New(None))
        );
        assert_eq!(
            handler.handle_special_commands("/new planning"),
            Some(InputCommand::New(Some("planning".to_string())))
        );
        assert_eq!(
            handler.handle_special_commands("/save"),
            Some(InputCommand::Save(None))
        );
        assert_eq!(
            handler.handle_special_commands("/save ./debug/session.md"),
            Some(InputCommand::Save(Some("./debug/session.md".to_string())))
        );
        assert_eq!(
            handler.handle_special_commands("/list"),
            Some(InputCommand::Sessions)
        );
        assert_eq!(
            handler.handle_special_commands("/sessions"),
            Some(InputCommand::Sessions)
        );
        assert_eq!(
            handler.handle_special_commands("/use abc123"),
            Some(InputCommand::Use("abc123".to_string()))
        );
        assert_eq!(handler.handle_special_commands("/unknown"), None);
        assert_eq!(handler.handle_special_commands("/use"), None);
    }
}
