// 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 and state management.
//!
//! This module defines structures and enums for representing task states,
//! progress, notifications, and detailed task information used throughout the
//! request system.

use std::collections::HashMap;

use ipc::parcel::Deserialize;

use crate::config::{Action, FormItem, Mode, Version};
use crate::file::FileSpec;

/// Enumeration of possible task states.
///
/// Represents the lifecycle stages of a network task, from initialization
/// through execution to completion or failure.
#[derive(Clone, Debug)]
#[repr(u32)]
pub enum State {
    /// Task has been initialized but not yet scheduled.
    Initialized = 0x00,
    /// Task is waiting to be executed.
    Waiting = 0x10,
    /// Task is currently being executed.
    Running = 0x20,
    /// Task is retrying after a previous failure.
    Retrying = 0x21,
    /// Task execution has been temporarily paused.
    Paused = 0x30,
    /// Task execution has been permanently stopped.
    Stopped = 0x31,
    /// Task has completed successfully.
    Completed = 0x40,
    /// Task has failed to complete.
    Failed = 0x41,
    /// Task has been removed from the system.
    Removed = 0x50,
    /// Special value representing any state in filter operations.
    Any = 0x61,
}

impl From<u32> for State {
    /// Converts a u32 value to a `State` enum variant.
    ///
    /// # Notes
    ///
    /// Any value that doesn't match a defined state will be mapped to
    /// `State::Any`.
    fn from(value: u32) -> Self {
        match value {
            0x00 => State::Initialized,
            0x10 => State::Waiting,
            0x20 => State::Running,
            0x21 => State::Retrying,
            0x30 => State::Paused,
            0x31 => State::Stopped,
            0x40 => State::Completed,
            0x41 => State::Failed,
            0x50 => State::Removed,
            _ => State::Any,
        }
    }
}

/// Types of notifications that can be subscribed to for task events.
#[repr(u32)]
#[derive(Debug)]
pub enum SubscribeType {
    /// Task has completed successfully.
    Completed = 0,
    /// Task has failed to complete.
    Failed,
    /// HTTP headers have been received.
    HeaderReceive,
    /// Task execution has been paused.
    Pause,
    /// Progress update for the task.
    Progress,
    /// Task has been removed.
    Remove,
    /// Task execution has been resumed.
    Resume,
    /// HTTP response has been received.
    Response,
    /// A fault has occurred on the task.
    FaultOccur,
    /// The task has entered a waiting state.
    Wait,
    /// Marker for the end of the enum.
    Butt,
}

impl From<u32> for SubscribeType {
    /// Converts a u32 value to a `SubscribeType` enum variant.
    ///
    /// # Panics
    ///
    /// Panics if the value is not a valid `SubscribeType` variant.
    fn from(value: u32) -> Self {
        match value {
            0 => SubscribeType::Completed,
            1 => SubscribeType::Failed,
            2 => SubscribeType::HeaderReceive,
            3 => SubscribeType::Pause,
            4 => SubscribeType::Progress,
            5 => SubscribeType::Remove,
            6 => SubscribeType::Resume,
            7 => SubscribeType::Response,
            8 => SubscribeType::FaultOccur,
            9 => SubscribeType::Wait,
            10 => SubscribeType::Butt,
            _ => unimplemented!(),
        }
    }
}

/// Coarse fault category for a failed task, derived from a finer-grained `Reason`.
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum Faults {
    /// Fault that does not fit any other category.
    Others = 0xFF,
    /// Network connection was disconnected.
    Disconnected = 0x00,
    /// Operation timed out.
    Timeout = 0x10,
    /// Protocol-level error.
    Protocol = 0x20,
    /// Invalid request parameter.
    Param = 0x30,
    /// File system input/output error.
    Fsio = 0x40,
    /// Domain name resolution error.
    Dns = 0x50,
    /// TCP-level error.
    Tcp = 0x60,
    /// SSL/TLS-level error.
    Ssl = 0x70,
    /// Redirect handling error.
    Redirect = 0x80,
    /// Transfer speed fell below the configured minimum.
    LowSpeed = 0x90,
}

impl From<u32> for Faults {
    fn from(value: u32) -> Self {
        match value {
            0xFF => Faults::Others,
            0x00 => Faults::Disconnected,
            0x10 => Faults::Timeout,
            0x20 => Faults::Protocol,
            0x30 => Faults::Param,
            0x40 => Faults::Fsio,
            0x50 => Faults::Dns,
            0x60 => Faults::Tcp,
            0x70 => Faults::Ssl,
            0x80 => Faults::Redirect,
            0x90 => Faults::LowSpeed,
            _ => unimplemented!(),
        }
    }
}

impl From<Reason> for Faults {
    fn from(reason: Reason) -> Self {
        match reason {
            Reason::NetworkOffline
            | Reason::NetworkApp
            | Reason::NetworkAccount
            | Reason::NetworkAppAccount => Faults::Disconnected,
            Reason::BuildClientFailed | Reason::BuildRequestFailed => Faults::Param,
            Reason::GetFilesizeFailed | Reason::IoError => Faults::Fsio,
            Reason::ContinuousTaskTimeout => Faults::Timeout,
            Reason::ConnectError => Faults::Tcp,
            Reason::RequestError | Reason::ProtocolError | Reason::UnsupportRangeRequest => {
                Faults::Protocol
            }
            Reason::RedirectError => Faults::Redirect,
            Reason::DNS => Faults::Dns,
            Reason::TCP => Faults::Tcp,
            Reason::SSL => Faults::Ssl,
            Reason::LowSpeed => Faults::LowSpeed,
            _ => Faults::Others,
        }
    }
}

/// Fine-grained reason describing why a task stopped, paused, or waited.
#[derive(Debug, Copy, Clone)]
pub enum Reason {
    /// Task completed successfully.
    ReasonOk = 0,
    /// Task has been running for over one month.
    TaskSurvivalOneMonth,
    /// Task has been waiting for network for over one day.
    WaittingNetworkOneDay,
    /// Task was superseded by a newer front-task.
    StoppedNewFrontTask,
    /// Running task count reached the configured limit.
    RunningTaskMeetLimits,
    /// Task was stopped by an explicit user operation.
    UserOperation,
    /// Owning application moved to background or was terminated.
    AppBackgroundOrTerminate,
    /// Device is offline with no network.
    NetworkOffline,
    /// Current network type is not supported for the task.
    UnsupportedNetworkType,
    /// HTTP client could not be built.
    BuildClientFailed,
    /// HTTP request could not be built.
    BuildRequestFailed,
    /// File size could not be determined.
    GetFilesizeFailed,
    /// Continuous task execution timed out.
    ContinuousTaskTimeout,
    /// Connection error occurred.
    ConnectError,
    /// Generic request error.
    RequestError,
    /// Upload file error occurred.
    UploadFileError,
    /// Redirect handling error.
    RedirectError,
    /// Protocol-level error.
    ProtocolError,
    /// Input/output error.
    IoError,
    /// Range request is not supported.
    UnsupportRangeRequest,
    /// Error that does not match any specific reason.
    OthersError,
    /// Account was stopped.
    AccountStopped,
    /// Network changed during the task.
    NetworkChanged,
    /// Domain name resolution failed.
    DNS,
    /// TCP-level error.
    TCP,
    /// SSL/TLS-level error.
    SSL,
    /// Insufficient storage space.
    InsufficientSpace,
    /// Network is not available for the application.
    NetworkApp,
    /// Network is not available for the account.
    NetworkAccount,
    /// Application is not available for the account.
    AppAccount,
    /// Neither network, application, nor account is available.
    NetworkAppAccount,
    /// Transfer speed fell below the configured minimum.
    LowSpeed,
}

impl From<u32> for Reason {
    fn from(value: u32) -> Self {
        match value {
            0 => Reason::ReasonOk,
            1 => Reason::TaskSurvivalOneMonth,
            2 => Reason::WaittingNetworkOneDay,
            3 => Reason::StoppedNewFrontTask,
            4 => Reason::RunningTaskMeetLimits,
            5 => Reason::UserOperation,
            6 => Reason::AppBackgroundOrTerminate,
            7 => Reason::NetworkOffline,
            8 => Reason::UnsupportedNetworkType,
            9 => Reason::BuildClientFailed,
            10 => Reason::BuildRequestFailed,
            11 => Reason::GetFilesizeFailed,
            12 => Reason::ContinuousTaskTimeout,
            13 => Reason::ConnectError,
            14 => Reason::RequestError,
            15 => Reason::UploadFileError,
            16 => Reason::RedirectError,
            17 => Reason::ProtocolError,
            18 => Reason::IoError,
            19 => Reason::UnsupportRangeRequest,
            20 => Reason::OthersError,
            21 => Reason::AccountStopped,
            22 => Reason::NetworkChanged,
            23 => Reason::DNS,
            24 => Reason::TCP,
            25 => Reason::SSL,
            26 => Reason::InsufficientSpace,
            27 => Reason::NetworkApp,
            28 => Reason::NetworkAccount,
            29 => Reason::AppAccount,
            30 => Reason::NetworkAppAccount,
            31 => Reason::LowSpeed,
            _ => unimplemented!(),
        }
    }
}

/// Notification payload describing a fault that occurred on a task.
#[derive(Debug)]
pub struct FaultOccur {
    /// Identifier of the task that faulted.
    pub task_id: i32,
    /// Subscription type that triggered the notification.
    pub subscribe_type: SubscribeType,
    /// Coarse category of the fault.
    pub faults: Faults,
}

/// Notification payload describing a task entering a waiting state.
pub struct Wait {
    /// Identifier of the task that is waiting.
    pub task_id: i32,
    /// Reason the task is waiting.
    pub waiting_reason: WaitingReason,
}

/// Reason a task has entered a waiting state.
#[derive(Copy, Clone)]
#[repr(u32)]
pub enum WaitingReason {
    /// Task queue is full.
    TASK_QUEUE_FULL = 0x00,
    /// Current network does not match the task requirements.
    NETWORK_NOT_MATCH = 0x01,
    /// Owning application is in the background.
    APP_BACKGROUND = 0x02,
    /// User is inactive.
    USER_INACTIVATED = 0x03,
}

impl From<u32> for WaitingReason {
    fn from(value: u32) -> Self {
        match value {
            0x00 => WaitingReason::TASK_QUEUE_FULL,
            0x01 => WaitingReason::NETWORK_NOT_MATCH,
            0x02 => WaitingReason::APP_BACKGROUND,
            0x03 => WaitingReason::USER_INACTIVATED,
            _ => unimplemented!(),
        }
    }
}

/// HTTP response received for a task.
#[derive(Debug)]
pub struct Response {
    /// Unique identifier of the task associated with this response.
    pub task_id: String,
    /// Version identifier of the response format.
    pub version: String,
    /// HTTP status code returned by the server.
    pub status_code: i32,
    /// Textual reason phrase associated with the status code.
    pub reason: String,
    /// HTTP headers returned by the server.
    pub headers: HashMap<String, Vec<String>>,
}

/// Status information for a specific task file.
#[derive(Clone, Debug)]
pub struct TaskState {
    /// Path to the file being processed.
    pub path: String,
    /// HTTP response code for this file.
    pub response_code: u32,
    /// Additional status message for this file.
    pub message: String,
}

/// Progress information for a task.
///
/// Contains current state, processed bytes, and other progress metrics.
#[derive(Debug)]
pub struct Progress {
    /// Current state of the task.
    pub state: State,
    /// Index of the current file being processed.
    pub index: u32,
    /// Number of bytes processed in the current file.
    pub processed: u64,
    /// Total number of bytes processed across all files.
    pub total_processed: u64,
    /// Sizes of all files in the task (in bytes).
    pub sizes: Vec<i64>,
    /// Additional progress-related metadata.
    pub extras: HashMap<String, String>,
    /// Raw bytes of the response body received so far.
    pub body_bytes: Vec<u8>,
}

/// Data structure for task notifications.
///
/// Combines subscription type, task identifier, progress information,
/// and additional task details.
#[derive(Debug)]
pub struct NotifyData {
    /// Type of notification being sent.
    pub subscribe_type: SubscribeType,
    /// Unique identifier of the task.
    pub task_id: u32,
    /// Current progress information for the task.
    pub progress: Progress,
    /// Action type of the task.
    pub action: Action,
    /// Version of the task protocol.
    pub version: Version,
    /// Status information for each file in the task.
    pub task_states: Vec<TaskState>,
}

/// Detailed progress information for a task.
#[derive(Clone, Debug)]
pub struct InfoProgress {
    /// Common progress data shared across different progress representations.
    pub common_data: CommonProgress,
    /// Total size of the files (in bytes).
    pub sizes: Vec<i64>,
    /// Processed size for each individual file (in bytes).
    pub processed: Vec<usize>,
    /// Additional progress-related metadata.
    pub extras: HashMap<String, String>,
}

/// Common progress data shared across different progress representations.
#[derive(Clone, Debug)]
pub struct CommonProgress {
    /// Current state of the task (as a numeric value).
    pub state: u8,
    /// Index of the current file being processed.
    pub index: usize,
    /// Total number of bytes processed across all files.
    pub total_processed: usize,
}

/// Core task information with minimal overhead.
///
/// Contains essential task metadata needed for quick task identification and
/// management.
#[derive(Copy, Clone, Debug)]
pub struct CommonTaskInfo {
    /// Unique identifier of the task.
    pub task_id: u32,
    /// User ID of the task owner.
    pub uid: u64,
    /// Action type of the task (as a numeric value).
    pub action: u8,
    /// Operating mode of the task (as a numeric value).
    pub mode: u8,
    /// Creation time of the task (Unix timestamp).
    pub ctime: u64,
    /// Last modification time of the task (Unix timestamp).
    pub mtime: u64,
    /// Reason code for the current state.
    pub reason: u8,
    /// Whether the task progress can be accurately measured.
    pub gauge: bool,
    /// Whether the task will automatically retry on failure.
    pub retry: bool,
    /// Number of retry attempts made so far.
    pub tries: u32,
    /// Protocol version of the task.
    pub version: u8,
    /// Priority level of the task.
    pub priority: u32,
}

/// Comprehensive information about a network task.
///
/// Contains all details needed to represent and manage a network task,
/// including configuration, state, and progress information.
///
/// # Examples
///
/// ```rust
/// use request_core::file::FileSpec;
/// use request_core::info::TaskInfo;
///
/// // Access task details
/// fn process_task_info(task_info: &TaskInfo) {
///     println!("Task ID: {}", task_info.common_data.task_id);
///     println!("URL: {}", task_info.url);
///     println!(
///         "Progress: {}/{}",
///         task_info.progress.common_data.total_processed,
///         task_info.progress.sizes.iter().sum::<i64>()
///     );
/// }
/// ```
#[derive(Debug, Clone)]
pub struct TaskInfo {
    /// Bundle name of the task owner.
    pub bundle: String,
    /// URL of the network request.
    pub url: String,
    /// Request body data.
    pub data: String,
    /// Authentication token.
    pub token: String,
    /// Form data items for the request.
    pub form_items: Vec<FormItem>,
    /// File specifications for uploads or downloads.
    pub file_specs: Vec<FileSpec>,
    /// User-visible title of the task.
    pub title: String,
    /// User-visible description of the task.
    pub description: String,
    /// MIME type of the request or response.
    pub mime_type: String,
    /// Progress information for the task.
    pub progress: InfoProgress,
    /// Additional task metadata.
    pub extras: HashMap<String, String>,
    /// Common task information.
    pub common_data: CommonTaskInfo,
    /// Maximum allowed transfer speed (bytes per second).
    pub max_speed: i64,
}

impl Deserialize for TaskInfo {
    /// Deserializes a `TaskInfo` from an IPC parcel.
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails for any reason.
    fn deserialize(parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<Self> {
        let gauge = parcel.read::<bool>().unwrap();
        let retry = parcel.read::<bool>().unwrap();
        let action = parcel.read::<u32>().unwrap() as u8;
        let mode = parcel.read::<u32>().unwrap() as u8;
        let reason = parcel.read::<u32>().unwrap() as u8;
        let tries = parcel.read::<u32>().unwrap();

        // Parse user ID from string representation
        let uid = parcel.read::<String>().unwrap().parse::<u64>().unwrap_or(0);

        let bundle = parcel.read::<String>().unwrap();
        let url = parcel.read::<String>().unwrap();

        // Parse task ID from string representation
        let task_id = parcel.read::<String>().unwrap().parse::<u32>().unwrap_or(0);

        let title = parcel.read::<String>().unwrap();
        let mime_type = parcel.read::<String>().unwrap();
        let ctime = parcel.read::<u64>().unwrap();
        let mtime = parcel.read::<u64>().unwrap();
        let data = parcel.read::<String>().unwrap();
        let description = parcel.read::<String>().unwrap();
        let priority = parcel.read::<u32>().unwrap();

        // Read form items
        let form_items_len = parcel.read::<u32>().unwrap() as usize;
        let mut form_items = Vec::with_capacity(form_items_len);
        for _ in 0..form_items_len {
            let name = parcel.read::<String>().unwrap();
            let value = parcel.read::<String>().unwrap();
            form_items.push(FormItem { name, value });
        }

        // Read file specifications
        let file_specs_len = parcel.read::<u32>().unwrap() as usize;
        let mut file_specs = Vec::with_capacity(file_specs_len);
        for _ in 0..file_specs_len {
            let name = parcel.read::<String>().unwrap();
            let path = parcel.read::<String>().unwrap();
            let file_name = parcel.read::<String>().unwrap();
            let mime_type = parcel.read::<String>().unwrap();
            file_specs.push(FileSpec {
                name,
                path,
                file_name,
                mime_type,
                fd: None,
                is_user_file: false, // Assuming is_user_file is false by default
            });
        }

        // Read progress information
        let state = parcel.read::<u32>().unwrap() as u8;
        let index = parcel.read::<u32>().unwrap() as usize;
        let processed = parcel.read::<u64>().unwrap() as usize;
        let total_processed = parcel.read::<u64>().unwrap() as usize;
        let sizes = parcel.read::<Vec<i64>>().unwrap();

        // Read progress extras
        let extras_len = parcel.read::<u32>().unwrap() as usize;
        let mut progress_extras = HashMap::with_capacity(extras_len);
        for _ in 0..extras_len {
            let key = parcel.read::<String>().unwrap();
            let value = parcel.read::<String>().unwrap();
            progress_extras.insert(key, value);
        }

        // Read task extras
        let extras_len = parcel.read::<u32>().unwrap() as usize;
        let mut extras = HashMap::with_capacity(extras_len);
        for _ in 0..extras_len {
            let key = parcel.read::<String>().unwrap();
            let value = parcel.read::<String>().unwrap();
            extras.insert(key, value);
        }

        // Read protocol version
        let version = parcel.read::<u32>().unwrap() as u8;

        // Read task states for individual files
        let each_file_status_len = parcel.read::<u32>().unwrap() as usize;
        let mut task_states = Vec::with_capacity(each_file_status_len);
        for _ in 0..each_file_status_len {
            let path = parcel.read::<String>().unwrap();
            let reason = parcel.read::<u32>().unwrap() as u8;
            let message = parcel.read::<String>().unwrap();
            task_states.push(TaskState {
                path,
                response_code: reason as u32,
                message,
            });
        }

        // Construct common task information
        let common_data = CommonTaskInfo {
            task_id,
            uid,
            action,
            mode,
            ctime,
            mtime,
            reason,
            gauge,
            retry,
            tries,
            version,
            priority,
        };

        // Construct progress information
        let progress = InfoProgress {
            common_data: CommonProgress {
                state,
                index,
                total_processed,
            },
            sizes,
            processed: vec![processed; file_specs.len()],
            extras: progress_extras,
        };

        // Return constructed TaskInfo
        Ok(TaskInfo {
            bundle,
            url,
            data,
            token: String::new(), // Token is not serialized in this context
            form_items,
            file_specs,
            title,
            description,
            mime_type,
            progress,
            extras, // Extras are not serialized in this context
            common_data,
            max_speed: 0, // Max speed is not serialized in this context
        })
    }
}