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

//! Bridge module for API 9 request functionality.
//!
//! This module defines data structures and conversion traits to bridge between
//! the ETS interface and the underlying request core functionality. It provides
//! type definitions for download and upload configurations, tasks, and
//! information.

use std::collections::HashMap;

use request_core::config::{
    Action, FormItem, NetworkConfig, TaskConfig, TaskConfigBuilder, Version,
};
use request_core::file::FileSpec;
use request_core::info::{self, TaskInfo};

/// Configuration for a download task.
///
/// Represents the parameters needed to configure a download operation through
/// the ETS API.
#[ani_rs::ani]
pub struct DownloadConfig {
    /// The URL to download from.
    pub url: String,
    /// Optional HTTP headers to include in the request.
    pub header: Option<HashMap<String, String>>,
    /// Whether to allow downloads on metered networks.
    pub enable_metered: Option<bool>,
    /// Whether to allow downloads while roaming.
    pub enable_roaming: Option<bool>,
    /// Optional description of the download.
    pub description: Option<String>,
    /// Optional network type restriction.
    pub network_type: Option<i32>,
    /// Optional file path for saving the download.
    pub file_path: Option<String>,
    /// Optional title for the download.
    pub title: Option<String>,
    /// Whether to download in the background.
    pub background: Option<bool>,
}

/// Configuration for an upload task.
///
/// Represents the parameters needed to configure an upload operation through
/// the ETS API.
#[ani_rs::ani(path = "@ohos.request.request.UploadConfigInner")]
pub struct UploadConfig {
    /// The URL to upload to.
    pub url: String,
    /// HTTP headers to include in the request.
    pub header: Option<HashMap<String, String>>,
    /// The HTTP method to use for the upload.
    pub method: String,
    /// Optional index parameter.
    pub index: Option<i32>,
    /// Optional beginning byte offset for partial uploads.
    pub begins: Option<i64>,
    /// Optional ending byte offset for partial uploads.
    pub ends: Option<i64>,
    /// List of files to upload.
    pub files: Vec<File>,
    /// List of form data to include in the upload.
    pub data: Vec<RequestData>,
}

/// Represents a download task.
///
/// Provides a handle to interact with a download operation.
#[ani_rs::ani(path = "@ohos.request.request.DownloadTaskInner")]
pub struct DownloadTask {
    /// The unique identifier of the download task.
    pub task_id: String,
}

/// Represents an upload task.
///
/// Provides a handle to interact with an upload operation.
#[ani_rs::ani(path = "@ohos.request.request.UploadTaskInner")]
pub struct UploadTask {
    /// The unique identifier of the upload task.
    pub task_id: String,
}

/// Information about a download task.
///
/// Contains detailed information about the state and progress of a download
/// operation.
#[allow(non_snake_case)]
#[ani_rs::ani(path = "@ohos.request.request.DownloadInfoInner")]
pub struct DownloadInfo {
    /// Description of the download.
    pub description: String,
    /// Number of bytes already downloaded.
    pub downloaded_bytes: i64,
    /// Unique identifier of the download.
    pub download_id: i64,
    /// Reason code if the download failed.
    pub failed_reason: i32,
    /// Name of the downloaded file.
    pub file_name: String,
    /// Path where the file is saved.
    pub file_path: String,
    /// Reason code if the download is paused.
    pub paused_reason: i32,
    /// Current status of the download.
    pub status: i32,
    /// Target URI being downloaded.
    pub target_URI: String,
    /// Title of the download.
    pub download_title: String,
    /// Total size of the download in bytes.
    pub download_total_bytes: i64,
}

/// Represents a file to be uploaded.
///
/// Contains information about a file that will be included in an upload
/// request.
#[ani_rs::ani(path = "@ohos.request.request.FileInner")]
pub struct File {
    /// Original filename of the file.
    filename: String,
    /// Form field name for the file.
    name: String,
    /// URI pointing to the file location.
    uri: String,
    /// MIME type of the file.
    type_: String,
}

/// Converts from API File to core FileSpec.
///
/// Transforms the ETS file representation into the core file specification
/// used by the request service.
impl From<File> for FileSpec {
    fn from(value: File) -> Self {
        FileSpec {
            file_name: value.filename,
            name: value.name,
            path: value.uri,
            mime_type: value.type_,
            is_user_file: false,
            fd: None,
        }
    }
}

/// Represents a request data item for form submissions.
///
/// Used to transfer form field data from the ETS API to the core service.
#[ani_rs::ani(path = "@ohos.request.request.RequestDataInner")]
pub struct RequestData {
    /// Name of the form field.
    name: String,
    /// Value of the form field.
    value: String,
}

/// Converts from API RequestData to core FormItem.
///
/// Transforms the ETS request data representation into the core form item
/// used for form submissions.
impl From<RequestData> for FormItem {
    fn from(value: RequestData) -> Self {
        FormItem {
            name: value.name,
            value: value.value,
        }
    }
}

/// State of an individual upload sub-task reported back to the caller.
#[ani_rs::ani(path = "@ohos.request.request.TaskStateInner")]
#[derive(Clone)]
pub struct TaskState {
    /// Path associated with the task.
    path: String,
    /// HTTP response code from the server.
    response_code: i32,
    /// Status message associated with the task.
    message: String,
}

impl From<request_core::info::TaskState> for TaskState {
    fn from(value: request_core::info::TaskState) -> Self {
        TaskState {
            path: value.path,
            response_code: value.response_code as i32,
            message: value.message,
        }
    }
}

impl From<DownloadConfig> for TaskConfig {
    fn from(config: DownloadConfig) -> Self {
        // Create builder configured for API9
        let mut config_builder = TaskConfigBuilder::new(Version::API9);

        // Set required URL
        config_builder.url(config.url);

        // Add optional parameters if provided
        if let Some(headers) = config.header {
            config_builder.headers(headers);
        }
        if let Some(enable_metered) = config.enable_metered {
            config_builder.metered(enable_metered);
        }
        if let Some(enable_roaming) = config.enable_roaming {
            config_builder.roaming(enable_roaming);
        }
        if let Some(network_type) = config.network_type {
            const NETWORK_MOBILE: i32 = 0x00000001;
            const NETWORK_WIFI: i32 = 0x00010000;
            let network_type = match network_type {
                NETWORK_MOBILE => NetworkConfig::Cellular,
                NETWORK_WIFI => NetworkConfig::Wifi,
                _ => NetworkConfig::Any,
            };
            config_builder.network_type(network_type);
        }
        if let Some(description) = config.description {
            config_builder.description(description);
        }
        config_builder.title(config.title.clone().unwrap_or("download".to_string()));
        if let Some(background) = config.background {
            config_builder.background(background);
        }
        if let Some(file_path) = config.file_path {
            config_builder.file_path(file_path);
        }
        config_builder.action(Action::Download);

        // Build the final task configuration
        config_builder.build()
    }
}

/// Converts from `TaskInfo` to `DownloadInfo`.
///
/// Transforms the core task information into the format expected by the ETS
/// API.
impl From<TaskInfo> for DownloadInfo {
    fn from(info: TaskInfo) -> Self {
        DownloadInfo {
            // Direct field mappings
            description: info.description,
            // Extract first element from arrays (assuming single-file downloads)
            downloaded_bytes: info.progress.processed[0] as i64,
            // Convert ID types as needed
            download_id: info.common_data.task_id as i64,
            failed_reason: info.common_data.reason as i32,
            // Extract file information from first file spec
            file_name: info.file_specs[0].file_name.clone(),
            file_path: info.file_specs[0].path.clone(),
            // Reason code used for both failure and pause states
            paused_reason: info.common_data.reason as i32,
            status: info.progress.common_data.state as i32,
            target_URI: info.url,
            download_title: info.title,
            // Get total size from progress information
            download_total_bytes: info.progress.sizes[0] as i64,
        }
    }
}

/// Converts from API UploadConfig to core TaskConfig.
///
/// Transforms the ETS upload configuration into the core task configuration
/// format, setting appropriate defaults for upload operations.
impl From<UploadConfig> for TaskConfig {
    fn from(config: UploadConfig) -> Self {
        let mut config_builder = TaskConfigBuilder::new(Version::API9);
        config_builder.url(config.url);
        if let Some(headers) = config.header {
            config_builder.headers(headers);
        }
        let method = config.method.to_uppercase();
        if method == "POST" || method == "PUT" {
            config_builder.method(method);
        } else {
            config_builder.method("POST".to_string());
        }
        if let Some(index) = config.index {
            config_builder.index(index);
        }
        if let Some(begins) = config.begins {
            config_builder.begins(begins);
        }
        if let Some(ends) = config.ends {
            config_builder.ends(ends);
        }
        config_builder.files(config.files.into_iter().map(Into::into).collect());
        config_builder.data(config.data.into_iter().map(Into::into).collect());
        config_builder.action(Action::Upload);
        config_builder.title("upload".to_string());
        config_builder.build()
    }
}