// Copyright (C) 2024 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Module for HTTP request task management.
//!
//! This module provides types and functionality for managing asynchronous HTTP
//! request tasks, including starting, canceling, resetting, and querying their
//! status.

use std::collections::HashMap;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};

use cxx::SharedPtr;
use request_utils::error;
use request_utils::task_id::TaskId;

use crate::info::DownloadInfoMgr;
use crate::request::RequestCallback;
use crate::response::Response;
use crate::wrapper::ffi::{HttpClientRequest, HttpClientTask, NewHttpClientTask, OnCallback};
use crate::wrapper::CallbackWrapper;

/// A handle to an asynchronous HTTP request task.
///
/// Provides control over an ongoing HTTP request, allowing operations like
/// starting, canceling, and resetting the request, as well as accessing
/// response data.
///
/// # Examples
///
/// ```
/// use netstack_rs::{Request, RequestTask};
///
/// // Create a request
/// let mut request = Request::builder()
///     .uri("https://example.com")
///     .method("GET")
///     .build();
///
/// // Create a task from the request
/// let mut task = request.task().unwrap();
///
/// // Start the task
/// if task.start() {
///     println!("Task started successfully");
/// }
///
/// // Later, if needed, cancel the task
/// task.cancel();
/// ```
#[derive(Clone)]
pub struct RequestTask {
    /// Shared reference to the underlying FFI task object
    inner: Arc<Mutex<SharedPtr<HttpClientTask>>>,
    /// Flag indicating if the task should be reset
    reset: Arc<AtomicBool>,
}

// SAFETY: The inner HttpClientTask is thread-safe through Mutex and Arc
unsafe impl Send for RequestTask {}
unsafe impl Sync for RequestTask {}

/// The current status of a request task.
///
/// # Examples
///
/// ```
/// use netstack_rs::{Request, TaskStatus};
///
/// // Create a request and task
/// let mut request = Request::builder().uri("https://example.com").build();
/// let mut task = request.task().unwrap();
///
/// // Check initial status
/// let status = task.status();
/// println!("Initial status: {:?}", status);
///
/// // Start the task
/// task.start();
///
/// // Check if task is running
/// if let TaskStatus::Running = task.status() {
///     println!("Task is now running");
/// }
/// ```
#[derive(Debug, Default)]
pub enum TaskStatus {
    /// The task is idle and not currently running
    Idle,
    /// The task is actively running (default state)
    #[default]
    Running,
}

impl RequestTask {
    /// Creates a new RequestTask from an HTTP request.
    ///
    /// # Arguments
    ///
    /// * `request` - The prepared HTTP request to execute
    ///
    /// # Returns
    ///
    /// `Some(RequestTask)` if creation succeeded, `None` if creation failed
    pub(crate) fn from_http_request(request: &HttpClientRequest) -> Option<Self> {
        let http_task = NewHttpClientTask(request);
        // Check if task creation failed
        if http_task.is_null() {
            error!("from_http_request NewHttpClientTask return null.");
            return None;
        }
        // Wrap the FFI task in thread-safe containers
        Some(Self {
            inner: Arc::new(Mutex::new(http_task)),
            reset: Arc::new(AtomicBool::new(false)),
        })
    }

    /// Creates a RequestTask from a raw FFI task pointer.
    ///
    /// # Arguments
    ///
    /// * `inner` - The raw FFI task pointer
    pub(crate) fn from_ffi(inner: SharedPtr<HttpClientTask>) -> Self {
        Self {
            inner: Arc::new(Mutex::new(inner)),
            reset: Arc::new(AtomicBool::new(false)),
        }
    }

    /// Starts execution of the request task.
    ///
    /// # Returns
    ///
    /// `true` if the task started successfully, `false` otherwise
    ///
    /// # Safety
    ///
    /// Uses unsafe code to convert the shared pointer to a mutable pointer for
    /// the FFI API.
    pub fn start(&mut self) -> bool {
        unsafe {
            // Convert from const to mutable pointer for FFI compatibility
            let ptr = self.inner.lock().unwrap().as_ref().unwrap() as *const HttpClientTask
                as *mut HttpClientTask;
            // Create a pinned mutable reference to call the Start method
            Pin::new_unchecked(ptr.as_mut().unwrap()).Start()
        }
    }

    /// Cancels the ongoing request task.
    ///
    /// Terminates the request if it is in progress.
    pub fn cancel(&self) {
        // Clone the task to avoid holding the lock during cancellation
        let task = self.inner.lock().unwrap().clone();
        Self::pin_mut(&task).Cancel();
    }

    /// Resets the task for potential reuse.
    ///
    /// Cancels any ongoing operation and prepares the task for restarting with
    /// new parameters. Uses atomic compare-and-exchange to ensure reset is
    /// only performed once.
    pub fn reset(&self) {
        // Only perform reset if not already resetting
        if self
            .reset
            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
            .is_ok()
        {
            self.cancel();
        }
    }

    /// Gets the current status of the task.
    ///
    /// # Returns
    ///
    /// The current `TaskStatus` of the request
    pub fn status(&mut self) -> TaskStatus {
        // Clone the task to avoid holding the lock during status check
        let task = self.inner.lock().unwrap().clone();
        // Try to convert the status code, defaulting to Running if conversion fails
        Self::pin_mut(&task)
            .GetStatus()
            .try_into()
            .unwrap_or_default()
    }

    /// Gets the response from the completed task.
    ///
    /// # Returns
    ///
    /// A `Response` object containing the HTTP response data
    pub fn response(&mut self) -> Response {
        // Clone the task to maintain ownership while creating the response
        let task = self.inner.lock().unwrap().clone();
        Response::from_shared(task)
    }

    /// Gets all response headers as a case-insensitive HashMap.
    ///
    /// Header names are converted to lowercase for consistent access.
    pub fn headers(&mut self) -> HashMap<String, String> {
        // Delegate to the response object's headers method
        self.response().headers()
    }

    /// Sets the callback handler for this task.
    ///
    /// # Arguments
    ///
    /// * `callback` - The callback implementation
    /// * `info_mgr` - Download info manager for performance tracking
    /// * `task_id` - Unique identifier for this task
    pub(crate) fn set_callback(
        &mut self,
        callback: Box<dyn RequestCallback + 'static>,
        info_mgr: Arc<DownloadInfoMgr>,
        task_id: TaskId,
    ) {
        let task = self.inner.lock().unwrap();
        OnCallback(
            &task,
            Box::new(CallbackWrapper::from_callback(
                callback,
                self.reset.clone(),
                Arc::downgrade(&self.inner),
                task_id,
                info_mgr,
                0,
            )),
        );
    }

    /// Helper method to get a pinned mutable reference to the underlying task.
    ///
    /// # Arguments
    ///
    /// * `ptr` - Shared pointer to the FFI task object
    ///
    /// # Returns
    ///
    /// Pinned mutable reference to the task
    ///
    /// # Safety
    ///
    /// Assumes the pointer is non-null and valid for the lifetime of the
    /// returned reference.
    pub(crate) fn pin_mut(ptr: &SharedPtr<HttpClientTask>) -> Pin<&mut HttpClientTask> {
        // Convert from const to mutable pointer for FFI compatibility
        let ptr = ptr.as_ref().unwrap() as *const HttpClientTask as *mut HttpClientTask;
        // Safety: Assumes the pointer is non-null and valid
        unsafe { Pin::new_unchecked(ptr.as_mut().unwrap()) }
    }
}

#[cfg(test)]
mod ut_task {
    include!("../tests/ut/ut_task.rs");
}