// Copyright (C) 2023 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.

//! Task information definitions.
//!
//! Defines `TaskInfo`, `CommonTaskInfo`, `State`, `InfoSet`, and dump types
//! that describe a task's runtime state, progress, and metadata, along with
//! helpers to build FFI-compatible data sets and notification payloads.

use std::collections::HashMap;

/// Task state enumeration.
pub use ffi::State;

use super::notify::{EachFileStatus, NotifyData, Progress};
use crate::task::config::{Action, Version};
use crate::task::reason::Reason;
use crate::utils::c_wrapper::{CFileSpec, CFormItem};
use crate::utils::form_item::{FileSpec, FormItem};
use crate::utils::hashmap_to_string;

/// Contains comprehensive information about a download/upload task.
#[derive(Debug, Clone)]
pub(crate) struct TaskInfo {
    /// Bundle name of the application that created the task.
    pub(crate) bundle: String,
    /// URL for the network request.
    pub(crate) url: String,
    /// Request payload data.
    pub(crate) data: String,
    /// Authentication token for the task.
    pub(crate) token: String,
    /// Form items to be included in the request.
    pub(crate) form_items: Vec<FormItem>,
    /// File specifications for download/upload.
    pub(crate) file_specs: Vec<FileSpec>,
    /// Title of the task.
    pub(crate) title: String,
    /// Description of the task.
    pub(crate) description: String,
    /// MIME type of the content.
    pub(crate) mime_type: String,
    /// Current progress of the task.
    pub(crate) progress: Progress,
    /// Additional task-specific parameters.
    pub(crate) extras: HashMap<String, String>,
    /// Common task metadata.
    pub(crate) common_data: CommonTaskInfo,
    /// Maximum speed limit in bytes per second.
    pub(crate) max_speed: i64,
    /// Time when the task was created.
    pub(crate) task_time: u64,
}

impl TaskInfo {
    /// Creates a new `TaskInfo` with default values.
    pub(crate) fn new() -> Self {
        Self {
            bundle: "".to_string(),
            url: "".to_string(),
            data: "".to_string(),
            token: "".to_string(),
            form_items: vec![],
            file_specs: vec![],
            title: "".to_string(),
            description: "".to_string(),
            mime_type: "".to_string(),
            // Has at least one progress size.
            progress: Progress::new(vec![0]),
            extras: HashMap::new(),
            common_data: CommonTaskInfo::new(),
            max_speed: 0,
            task_time: 0,
        }
    }

    /// Gets the user ID associated with this task.
    pub(crate) fn uid(&self) -> u64 {
        self.common_data.uid
    }

    /// Gets the MIME type of the task content.
    pub(crate) fn mime_type(&self) -> String {
        self.mime_type.clone()
    }

    /// Gets the action type (download/upload) for this task.
    pub(crate) fn action(&self) -> Action {
        Action::from(self.common_data.action)
    }

    /// Gets the authentication token for this task.
    pub(crate) fn token(&self) -> String {
        self.token.clone()
    }
}

/// Common metadata shared across different task representations.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub(crate) struct CommonTaskInfo {
    /// Unique identifier for the task.
    pub(crate) task_id: u32,
    /// User ID that owns the task.
    pub(crate) uid: u64,
    /// Action type encoded as a byte (0 for download, 1 for upload).
    pub(crate) action: u8,
    /// Operating mode encoded as a byte.
    pub(crate) mode: u8,
    /// Creation time in milliseconds since epoch.
    pub(crate) ctime: u64,
    /// Modification time in milliseconds since epoch.
    pub(crate) mtime: u64,
    /// Reason code for current state.
    pub(crate) reason: u8,
    /// Whether progress can be tracked accurately.
    pub(crate) gauge: bool,
    /// Whether automatic retries are enabled.
    pub(crate) retry: bool,
    /// Number of retry attempts made.
    pub(crate) tries: u32,
    /// API version used for this task.
    pub(crate) version: u8,
    /// Task priority level.
    pub(crate) priority: u32,
}

impl CommonTaskInfo {
    /// Creates a new `CommonTaskInfo` with default values.
    pub(crate) fn new() -> Self {
        Self {
            task_id: 0,
            uid: 0,
            action: 0,
            mode: 0,
            ctime: 0,
            mtime: 0,
            reason: 0,
            gauge: false,
            retry: false,
            tries: 0,
            version: 0,
            priority: 0,
        }
    }
}

/// Set of task information prepared for FFI calls.
pub(crate) struct InfoSet {
    /// Form items converted to C-compatible format.
    pub(crate) form_items: Vec<CFormItem>,
    /// File specifications converted to C-compatible format.
    pub(crate) file_specs: Vec<CFileSpec>,
    /// JSON string representation of file sizes.
    pub(crate) sizes: String,
    /// JSON string representation of processed bytes.
    pub(crate) processed: String,
    /// JSON string representation of extra parameters.
    pub(crate) extras: String,
}

// C++ interoperability bridge for task state enumeration
#[cxx::bridge(namespace = "OHOS::Request")]
mod ffi {
    #[derive(Clone, Copy, PartialEq, Debug)]
    #[repr(u8)]
    /// Represents the current state of a task.
    pub enum State {
        /// Task has been initialized but not yet started.
        Initialized = 0x00,
        /// Task is waiting to run.
        Waiting = 0x10,
        /// Task is actively running.
        Running = 0x20,
        /// Task is retrying after a failure.
        Retrying = 0x21,
        /// Task has been paused by the user or system.
        Paused = 0x30,
        /// Task has been stopped by the user or system.
        Stopped = 0x31,
        /// Task has completed successfully.
        Completed = 0x40,
        /// Task has failed to complete.
        Failed = 0x41,
        /// Task has been removed from the system.
        Removed = 0x50,
        /// Wildcard value used for filtering any state.
        Any = 0x61,
    }
}

/// Contains information needed to update a task's state.
#[derive(Debug)]
pub(crate) struct UpdateInfo {
    /// New modification time.
    pub(crate) mtime: u64,
    /// New reason code.
    pub(crate) reason: u8,
    /// Updated retry count.
    pub(crate) tries: u32,
    /// Updated MIME type.
    pub(crate) mime_type: String,
    /// Updated progress information.
    pub(crate) progress: Progress,
}

impl From<u8> for State {
    /// Converts a byte value to a `State` enum variant.
    ///
    /// # Notes
    /// Values not explicitly mapped default to `State::Any`.
    fn from(value: u8) -> Self {
        match value {
            0 => State::Initialized,
            16 => State::Waiting,
            32 => State::Running,
            33 => State::Retrying,
            48 => State::Paused,
            49 => State::Stopped,
            64 => State::Completed,
            65 => State::Failed,
            80 => State::Removed,
            _ => State::Any,
        }
    }
}

impl TaskInfo {
    /// Builds an `InfoSet` for FFI communication from this `TaskInfo`.
    ///
    /// Converts various components to C-compatible formats for interop.
    pub(crate) fn build_info_set(&self) -> InfoSet {
        InfoSet {
            form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(),
            file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(),
            sizes: format!("{:?}", self.progress.sizes),
            processed: format!("{:?}", self.progress.processed),
            extras: hashmap_to_string(&self.extras),
        }
    }

    /// Creates a list of `EachFileStatus` objects representing the status of
    /// each file. When the task failed due to a protocol error, the message
    /// is enriched with the specific HTTP status code.
    pub(crate) fn build_each_file_status(&self) -> Vec<EachFileStatus> {
        let mut statuses = EachFileStatus::create_each_file_status(
            &self.file_specs,
            self.progress.common_data.index,
            self.common_data.reason.into(),
        );
        let reason = Reason::from(self.common_data.reason);
        if reason == Reason::ProtocolError {
            let http_code = super::http_error_registry::take_http_status_code(self.common_data.task_id);
            if http_code > 0 {
                let phrase = super::http_error_registry::http_status_reason_phrase(http_code);
                let detailed = format!("Http protocol error: {} {}", http_code, phrase);
                for s in &mut statuses {
                    if s.reason == Reason::ProtocolError {
                        s.message = detailed.clone();
                    }
                }
            }
        }
        statuses
    }

    /// Builds a `NotifyData` object for status notifications.
    pub(crate) fn build_notify_data(&self) -> NotifyData {
        NotifyData {
            bundle: self.bundle.clone(),
            progress: self.progress.clone(),
            action: Action::from(self.common_data.action),
            version: Version::from(self.common_data.version),
            each_file_status: self.build_each_file_status(),
            task_id: self.common_data.task_id,
            uid: self.common_data.uid,
        }
    }
}

/// Container for multiple task information dumps.
#[derive(Debug)]
pub(crate) struct DumpAllInfo {
    /// List of individual task dumps.
    pub(crate) vec: Vec<DumpAllEachInfo>,
}

/// Contains minimal information for a single task in a dump.
#[derive(Debug)]
pub(crate) struct DumpAllEachInfo {
    /// Task identifier.
    pub(crate) task_id: u32,
    /// Action type (download/upload).
    pub(crate) action: Action,
    /// Current task state.
    pub(crate) state: State,
    /// Reason for current state.
    pub(crate) reason: Reason,
}

/// Contains detailed information for a single task dump.
#[derive(Debug)]
pub(crate) struct DumpOneInfo {
    /// Task identifier.
    pub(crate) task_id: u32,
    /// Action type (download/upload).
    pub(crate) action: Action,
    /// Current task state.
    pub(crate) state: State,
    /// Reason for current state.
    pub(crate) reason: Reason,
}

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