use std::time::Instant;

use metrics::{counter, histogram};
use mlua::{Lua, Result as LuaResult, Value};

use crate::irc::event::IrcEvent;
use crate::lua::sandbox::reset_instruction_counter;

pub fn dispatch_event(lua: &Lua, event: &IrcEvent) -> LuaResult<()> {
    reset_instruction_counter(lua)?;

    let handler_name = event.handler_name();
    let globals = lua.globals();

    let handler: Value = globals.get(handler_name)?;
    if handler.is_nil() {
        return Ok(());
    }

    let func = match handler {
        Value::Function(f) => f,
        _ => return Ok(()),
    };

    let start = Instant::now();

    let result = match event {
        IrcEvent::Message { nick, channel, text } => {
            func.call::<()>((nick.as_str(), channel.as_str(), text.as_str()))
        }
        IrcEvent::Join { nick, channel } => {
            func.call::<()>((nick.as_str(), channel.as_str()))
        }
        IrcEvent::Part { nick, channel, reason } => {
            func.call::<()>((nick.as_str(), channel.as_str(), reason.as_deref().unwrap_or("")))
        }
        IrcEvent::Kick { nick, channel, target, reason } => {
            func.call::<()>((nick.as_str(), channel.as_str(), target.as_str(), reason.as_deref().unwrap_or("")))
        }
        IrcEvent::Nick { old_nick, new_nick } => {
            func.call::<()>((old_nick.as_str(), new_nick.as_str()))
        }
        IrcEvent::Notice { nick, target, text } => {
            func.call::<()>((nick.as_str(), target.as_str(), text.as_str()))
        }
    };

    let elapsed = start.elapsed().as_secs_f64();
    counter!("irc_now_bot_script_runs_total", "handler" => handler_name).increment(1);
    histogram!("irc_now_bot_script_duration_seconds", "handler" => handler_name).record(elapsed);

    if let Err(e) = result {
        counter!("irc_now_bot_script_errors_total", "handler" => handler_name).increment(1);
        return Err(e);
    }

    Ok(())
}
