#![allow(dead_code)]

use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};

use crate::platform::current::runtime::{ThreadHandle, current_thread_handle};

type CancelCallback = Box<dyn FnOnce() + Send + 'static>;

struct CompletionState<T> {
    owner: ThreadHandle,
    interested: AtomicBool,
    finished: AtomicBool,
    wake_queued: AtomicBool,
    result: Mutex<Option<T>>,
    waker: Mutex<Option<Waker>>,
    cancel: Mutex<Option<CancelCallback>>,
}

impl<T: Send + 'static> CompletionState<T> {
    fn queue_wake(self: &Arc<Self>) {
        if self.wake_queued.swap(true, Ordering::AcqRel) {
            return;
        }

        // Queue as a macrotask: I/O completion events belong to the macrotask
        // queue, consistent with the JavaScript event-loop model where I/O
        // callbacks are macrotasks and the microtask queue is thread-local only.
        let state = Arc::clone(self);
        if !self.owner.queue_task(move || {
            state.wake_queued.store(false, Ordering::Release);
            if let Some(waker) = state.waker.lock().unwrap().take() {
                waker.wake();
            }
        }) {
            self.wake_queued.store(false, Ordering::Release);
        }
    }
}

pub(crate) struct CompletionFuture<T> {
    state: Arc<CompletionState<T>>,
}

pub(crate) struct CompletionHandle<T> {
    state: Arc<CompletionState<T>>,
}

impl<T> Clone for CompletionHandle<T> {
    fn clone(&self) -> Self {
        Self {
            state: Arc::clone(&self.state),
        }
    }
}

pub(crate) fn completion<T: Send + 'static>(
    owner: ThreadHandle,
) -> (CompletionFuture<T>, CompletionHandle<T>) {
    owner.begin_async_operation();
    let state = Arc::new(CompletionState {
        owner,
        interested: AtomicBool::new(true),
        finished: AtomicBool::new(false),
        wake_queued: AtomicBool::new(false),
        result: Mutex::new(None),
        waker: Mutex::new(None),
        cancel: Mutex::new(None),
    });

    (
        CompletionFuture {
            state: Arc::clone(&state),
        },
        CompletionHandle { state },
    )
}

pub(crate) fn completion_for_current_thread<T: Send + 'static>()
-> (CompletionFuture<T>, CompletionHandle<T>) {
    completion(current_thread_handle())
}

impl<T: Send + 'static> CompletionHandle<T> {
    pub(crate) fn complete(self, value: T) {
        self.finish(Some(value));
    }

    pub(crate) fn finish(self, value: Option<T>) {
        if self.state.finished.swap(true, Ordering::AcqRel) {
            return;
        }

        let interested = self.state.interested.load(Ordering::Acquire);
        if interested {
            *self.state.result.lock().unwrap() = value;
            self.state.queue_wake();
        }

        let _ = self.state.cancel.lock().unwrap().take();
        self.state.owner.finish_async_operation();
    }

    pub(crate) fn set_cancel(&self, cancel: impl FnOnce() + Send + 'static) {
        *self.state.cancel.lock().unwrap() = Some(Box::new(cancel));
    }

    pub(crate) fn is_interested(&self) -> bool {
        self.state.interested.load(Ordering::Acquire)
    }
}

impl<T> Future for CompletionFuture<T> {
    type Output = T;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(value) = self.state.result.lock().unwrap().take() {
            return Poll::Ready(value);
        }

        *self.state.waker.lock().unwrap() = Some(cx.waker().clone());

        if let Some(value) = self.state.result.lock().unwrap().take() {
            let _ = self.state.waker.lock().unwrap().take();
            return Poll::Ready(value);
        }

        Poll::Pending
    }
}

impl<T> Drop for CompletionFuture<T> {
    fn drop(&mut self) {
        if !self.state.interested.swap(false, Ordering::AcqRel) {
            return;
        }

        let _ = self.state.result.lock().unwrap().take();
        let _ = self.state.waker.lock().unwrap().take();

        if self.state.finished.load(Ordering::Acquire) {
            return;
        }

        if let Some(cancel) = self.state.cancel.lock().unwrap().take() {
            // Delegate to the cancel callback (e.g. submit an io_uring cancel).
            // The actual I/O completion will eventually call handle.finish(),
            // which decrements pending_ops.
            cancel();
        } else {
            // No cancel callback was registered — this happens when submit_operation
            // failed before set_cancel could be called, leaving no path through
            // which finish() would run. Decrement pending_ops directly so the
            // runtime does not stall indefinitely waiting for an operation that
            // will never complete. The swap is atomic: if finish() races to set
            // finished first, the swap returns true and we skip the decrement.
            if !self.state.finished.swap(true, Ordering::AcqRel) {
                self.state.owner.finish_async_operation();
            }
        }
    }
}
