use std::future::Future;
use std::io;
use std::time::Duration;

use crate::addr::SockAddr;
use crate::submit::SendMsgPacket;
use crate::{Fd, FixedFd};

pub trait RecvMsgOutView {
    fn parse_with<R>(
        raw: &[u8],
        msghdr: &libc::msghdr,
        f: impl FnOnce(&[u8], &[u8]) -> R,
    ) -> Option<R>;
}

pub trait DriverLifecycle {
    fn drive(&mut self) -> io::Result<bool>;
    fn has_pending(&mut self) -> bool;
    fn park(&mut self) -> io::Result<()>;
    fn park_for(&mut self, duration: Duration) -> io::Result<()>;
    fn submit_only(&mut self) -> io::Result<()>;

    #[inline]
    fn arm_with_retry<F>(&mut self, mut op: F) -> bool
    where
        F: FnMut(&mut Self) -> bool,
    {
        if op(self) {
            return true;
        }
        let _ = self.submit_only();
        op(self)
    }
}

pub trait FixedBuffers {
    type Guard;
    fn fixed_guard(&mut self) -> Option<Self::Guard>;
    fn register_fixed(&mut self, fd: Fd) -> io::Result<Option<u32>>;
    fn unregister_fixed(&mut self, idx: u32);
}

pub trait ProvidedBufRing {
    type Provided: AsRef<[u8]> + 'static;
    fn provided_group(&mut self) -> Option<(u16, u32)>;
    fn provided(&mut self, bid: u16, len: usize) -> Option<Self::Provided>;
}

pub trait DriverConfig: Sized + Copy {
    fn for_profile<P: crate::profile::Profile>() -> Self;

    fn for_tcp_profile(ring_entries: u32, max_conn: usize, ingress_buf_cap: usize) -> Self;
    fn for_quic_udp(provided_buf_entries: u32, provided_buf_len: u32) -> Self;

    #[inline(always)]
    fn with_defer_taskrun(self, _enable: bool) -> Self {
        self
    }

    #[inline(always)]
    fn with_cpu_id(self, _cpu: Option<u16>) -> Self {
        self
    }
}

pub trait Backend: Sized {
    type Driver: DriverLifecycle + FixedBuffers + ProvidedBufRing + 'static;
    type Config: DriverConfig;
    type RecvMsgOut: RecvMsgOutView;
    type ProvidedBuf;

    fn new_driver(cfg: Self::Config) -> io::Result<Self::Driver>;
}

pub trait BackendToken: Copy + 'static {
    fn parts(self) -> (usize, u32);
    fn next_for_slot(index: usize, seq: &mut u32) -> Self;
    fn from_index_raw(index: usize, epoch: u32) -> Self;
}

pub enum Completion<B: CompletionBackend> {
    Accept {
        token: B::Token,
        result: i32,
        more: bool,
    },
    Recv {
        token: B::Token,
        result: i32,
        more: bool,
        bid: Option<u16>,
    },
    Write {
        token: B::Token,
        result: i32,
        notif: bool,
    },
}

impl<B: CompletionBackend> Completion<B> {
    #[inline(always)]
    pub fn buffer_select(&self) -> Option<u16> {
        match self {
            Self::Recv { bid, .. } => *bid,
            _ => None,
        }
    }
}

pub struct CurrentSlot<B: CompletionBackend> {
    driver: std::ptr::NonNull<B::Driver>,
    extra: *const (),
}

impl<B: CompletionBackend> Clone for CurrentSlot<B> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<B: CompletionBackend> Copy for CurrentSlot<B> {}

impl<B: CompletionBackend> CurrentSlot<B> {
    #[inline(always)]
    pub fn new(driver: std::ptr::NonNull<B::Driver>, extra: *const ()) -> Self {
        Self { driver, extra }
    }

    #[inline(always)]
    pub fn driver(self) -> std::ptr::NonNull<B::Driver> {
        self.driver
    }

    #[inline(always)]
    pub fn extra(self) -> *const () {
        self.extra
    }
}

pub struct ExternalDispatch<B: CompletionBackend> {
    pub state: *mut (),
    pub route: unsafe fn(*mut (), &mut B::Driver, Completion<B>),
    pub pump: unsafe fn(*mut (), &mut B::Driver),
}

impl<B: CompletionBackend> Clone for ExternalDispatch<B> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<B: CompletionBackend> Copy for ExternalDispatch<B> {}

pub trait CompletionBackend: Backend {
    type Token: BackendToken;

    fn current_slot() -> Option<CurrentSlot<Self>>;
    fn set_slot(slot: Option<CurrentSlot<Self>>);

    #[inline(always)]
    fn current_driver_mut() -> *mut Self::Driver {
        match Self::current_slot() {
            Some(slot) => slot.driver().as_ptr(),
            None => std::ptr::null_mut(),
        }
    }

    #[inline(always)]
    fn set_current(driver: *mut Self::Driver) {
        let slot = std::ptr::NonNull::new(driver).map(|d| CurrentSlot::new(d, std::ptr::null()));
        Self::set_slot(slot);
    }

    #[inline(always)]
    fn clear_current() {
        Self::set_slot(None);
    }

    fn install_external(aux: Option<ExternalDispatch<Self>>) -> Option<ExternalDispatch<Self>>;
    fn current_external() -> Option<ExternalDispatch<Self>>;

    fn drive_with<R: CompletionRouter<Self>>(
        driver: &mut Self::Driver,
        router: &mut R,
    ) -> io::Result<bool>;

    fn dispatch_completions<R: CompletionRouter<Self>>(driver: &mut Self::Driver, router: &mut R);
}

pub trait CompletionRouter<B: CompletionBackend> {
    fn on_complete(&mut self, driver: &mut B::Driver, ev: Completion<B>);
}

pub trait ProvidedRingOps {
    fn defer(&mut self, bid: u16);
    fn ptr_len(&self, bid: u16) -> (*mut u8, usize);
}

pub trait DriverOps: 'static {
    type Token: BackendToken;
    type ProvidedRing: ProvidedRingOps + 'static;
    type IoRequest<O: Unpin + 'static>: Future<Output = (io::Result<u32>, u32, O)> + Unpin + 'static;

    fn buf_group(&self) -> u16;
    fn provided_ring(&mut self) -> &mut Self::ProvidedRing;

    fn arm_accept_multi(&mut self, token: Self::Token, fd: Fd) -> bool;
    fn arm_recv_multi(&mut self, token: Self::Token, fd: FixedFd, buf_group: u16) -> bool;

    /// # Safety
    /// `msghdr` must remain valid until the multishot recv is cancelled.
    unsafe fn arm_recv_msg_multi(
        &mut self,
        token: Self::Token,
        fd: FixedFd,
        buf_group: u16,
        msghdr: *const libc::msghdr,
    ) -> bool;

    fn submit_send_tagged(
        &mut self,
        token: Self::Token,
        fd: FixedFd,
        ptr: *const u8,
        len: u32,
    ) -> bool;

    /// # Safety
    /// `msg` must remain valid until the submission completes.
    unsafe fn submit_send_msg_tagged(
        &mut self,
        token: Self::Token,
        fd: FixedFd,
        msg: *const libc::msghdr,
    ) -> bool;

    fn cancel_recv(&mut self, token: Self::Token);

    fn try_submit_connect_raw(
        &mut self,
        fd: Fd,
        addr: SockAddr,
    ) -> Result<Self::IoRequest<SockAddr>, (io::Error, SockAddr)>;

    fn try_submit_sendmsg_raw<P: SendMsgPacket + Unpin + 'static>(
        &mut self,
        fd: Fd,
        packet: P,
    ) -> Result<Self::IoRequest<P>, (io::Error, P)>;

    fn try_submit_send_raw<B: AsRef<[u8]> + Unpin + 'static>(
        &mut self,
        fd: Fd,
        buf: B,
    ) -> Result<Self::IoRequest<B>, (io::Error, B)>;
}

pub trait PoolDriver:
    CompletionBackend<Driver: DriverOps<Token = <Self as CompletionBackend>::Token>> + 'static
{
}

impl<B> PoolDriver for B
where
    B: CompletionBackend + 'static,
    B::Driver: DriverOps<Token = B::Token>,
{
}

pub type IoRequest<B, O> = <<B as Backend>::Driver as DriverOps>::IoRequest<O>;
pub type ProvidedRing<B> = <<B as Backend>::Driver as DriverOps>::ProvidedRing;
