use std::{num::NonZero, time::Duration};

use crate::*;
use ::tokio;

serde! {
    #[derive(Default)]
    pub struct Runtime {
        pub flavor: RuntimeFlavor,
        pub worker_threads: Option<NonZero<usize>>,
        pub max_blocking_threads: Option<NonZero<usize>>,
        pub thread_name: Option<String>,
        pub thread_stack_size: Option<usize>,
        #[serde_as(as = "Option<HumanTime>")]
        pub thread_keepalive: Option<Duration>,
        pub global_queue_interval: Option<NonZero<u32>>,
        pub event_interval: Option<u32>,
        pub enable_io: Option<bool>,
        pub max_io_events_per_tick: Option<usize>,
        pub enable_time: Option<bool>,
    }

    #[derive(Default)]
    pub enum RuntimeFlavor {
        CurrentThread,
        #[default]
        MultiThread,
    }
}

impl Runtime {
    pub fn builder(self) -> tokio::runtime::Builder {
        let Self {
            flavor,
            worker_threads,
            max_blocking_threads,
            thread_stack_size,
            thread_keepalive,
            global_queue_interval,
            event_interval,
            enable_io,
            max_io_events_per_tick,
            enable_time,
            thread_name,
        } = self;
        let mut b = match flavor {
            RuntimeFlavor::CurrentThread => tokio::runtime::Builder::new_current_thread(),
            RuntimeFlavor::MultiThread => tokio::runtime::Builder::new_multi_thread(),
        };
        (&mut b)
            .tap_opt(worker_threads, |b, v| b.worker_threads(v.get()))
            .tap_opt(max_blocking_threads, |b, v| b.max_blocking_threads(v.get()))
            .tap_opt(thread_name, |b, v| b.thread_name(v))
            .tap_opt(thread_stack_size, |b, v| b.thread_stack_size(v))
            .tap_opt(thread_keepalive, |b, v| b.thread_keep_alive(v))
            .tap_opt(global_queue_interval, |b, v| {
                b.global_queue_interval(v.get())
            })
            .tap_opt(event_interval, |b, v| b.event_interval(v))
            .tap_opt(max_io_events_per_tick, |b, v| b.max_io_events_per_tick(v));
        if let Some(true) = enable_io {
            b.enable_io();
        }
        if let Some(true) = enable_time {
            b.enable_time();
        }
        b
    }
}
