// 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.

//! Agent module for API 10.
//!
//! This module provides functions to manage download tasks in API 10,
//! including task creation, retrieval, removal, and search operations.

// use std::path::PathBuf;

use ani_rs::business_error::BusinessError;
use ani_rs::objects::{AniObject, AniRef};
use ani_rs::AniEnv;
use request_client::check::file::DownloadPathError;
use request_client::client::error::CreateTaskError;
use request_client::RequestClient;
use request_core::config::{TaskConfig, Version};
use request_core::filter::SearchFilter;
use request_utils::context::Context;

use crate::api10::bridge::{Config, Filter, Task, TaskInfo};
use crate::api10::callback::CallbackManager;
use crate::constant::*;
use crate::seq::TaskSeq;

/// Minimum allowed length for authentication tokens (in bytes).
const TOKEN_MIN_BYTES: usize = 8;
/// Maximum allowed length for authentication tokens (in bytes).
const TOKEN_MAX_BYTES: usize = 2048;

/// Validates task ID format.
///
/// Checks that the task ID is not empty and does not exceed the maximum length.
///
/// # Parameters
///
/// * `id` - Task ID to validate
///
/// # Returns
///
/// * `Ok(())` if the task ID is valid
/// * `Err(BusinessError)` if the task ID is empty or too long
#[ani_rs::native]
pub fn check_tid(id: String) -> Result<(), BusinessError> {
    if id.is_empty() {
        return Err(BusinessError::new(
            ExceptionErrorCode::E_PARAMETER_CHECK as i32,
            "Parameter verification failed, tid is empty".to_string(),
        ));
    }
    if id.len() > 32 {
        return Err(BusinessError::new(
            ExceptionErrorCode::E_TASK_NOT_FOUND as i32,
            "task not found error".to_string(),
        ));
    }
    Ok(())
}

/// Validates authentication token format.
///
/// Checks that the token length is within the allowed range.
///
/// # Parameters
///
/// * `token` - Authentication token to validate
///
/// # Returns
///
/// * `Ok(())` if the token is valid
/// * `Err(BusinessError)` if the token length is out of range
#[ani_rs::native]
pub fn check_token(token: String) -> Result<(), BusinessError> {
    if token.len() < TOKEN_MIN_BYTES || token.len() > TOKEN_MAX_BYTES {
        return Err(BusinessError::new(
            ExceptionErrorCode::E_PARAMETER_CHECK as i32,
            "Parameter verification failed, the length of token should between 8 and 2048 bytes"
                .to_string(),
        ));
    }
    Ok(())
}

/// Validates an API 10 task configuration and allocates a sequence ID.
///
/// # Returns
///
/// * `Ok(i64)` containing the generated sequence ID on success
/// * `Err(BusinessError)` if the configuration or download path is invalid
#[ani_rs::native]
pub fn check_config(env: &AniEnv, context: AniRef, config: Config) -> Result<i64, BusinessError> {
    let context = AniObject::from(context);
    // Generate a new sequential task ID for tracking
    let seq = TaskSeq::next().0.get();
    info!("Check Config, seq: {}", seq);
    let context = Context::new(env, &context);

    let want_agent = config.notification.as_ref().and_then(|n| {
        n.want_agent
            .as_ref()
            .map(|agent| Context::stringfy_want_agent(env, agent))
    });

    let mut config: TaskConfig = config.into();
    // TODO: CHECK NULLPTR
    config.bundle_type = context.get_bundle_type() as u32;
    config.bundle = context.get_bundle_name();
    config.notification.want_agent = want_agent;

    match RequestClient::get_instance().check_config(context, seq, config) {
        Ok(_) => Ok(seq as i64),
        Err(e) => {
            error!("Create task failed: {:?}", e);
            // Handle specific error types and return appropriate business errors
            match e {
                CreateTaskError::DownloadPath(err) => {
                    let (code, message): (i32, String) = match err {
                        DownloadPathError::InvalidPath => (401, "Invalid Path".to_string()),
                        DownloadPathError::EmptyPath => {
                            (13400001, "Invalid file or file system error, File path is empty".to_string())
                        }
                        DownloadPathError::TooLongPath => {
                            (13400001, "Invalid file or file system error, File path exceeds maximum allowed length".to_string())
                        }
                        DownloadPathError::BundleNameNotMap => (
                            13400001,
                            "Invalid file or file system error, File path bundle name does not match the application".to_string(),
                        ),
                        DownloadPathError::AlreadyExists => {
                            (13400001, "Invalid file or file system error, File already exists, set overwrite=true to replace".to_string())
                        }
                        DownloadPathError::CreateFile(e) => {
                            (13400001, format!("Invalid file or file system error, Failed to create file: {}", e))
                        }
                        DownloadPathError::SetPermission(e) => (
                            13400001,
                            format!("Invalid file or file system error, Failed to set file permissions: {}", e),
                        ),
                        DownloadPathError::AclAccess(e) => (
                            13400001,
                            format!("Invalid file or file system error, ACL permission denied: {}", e),
                        ),
                    };
                    Err(BusinessError::new(code, message))
                }
                CreateTaskError::Code(code) => {
                    Err(BusinessError::new_static(code, "Create Task Failed"))
                }
            }
        }
    }
}

/// Creates a new download task with the given configuration.
///
/// # Parameters
///
/// * `env` - The animation environment reference
/// * `context` - The application context
/// * `config` - The task configuration containing URL, save path, etc.
///
/// # Returns
///
/// * `Ok(Task)` if the task was successfully created
/// * `Err(BusinessError)` if there was an error during task creation
///
/// # Errors
///
/// Returns an error if:
/// * The download path is invalid (error code 401)
/// * There's a file system error (error code 13400001)
/// * Task creation fails with a specific error code
///
/// # Examples
///
/// ```rust
/// use ani_rs::objects::AniRef;
/// use ani_rs::AniEnv;
/// use request_api10::api10::agent::create;
/// use request_api10::api10::bridge::Config;
///
/// // Assuming env and context are properly initialized
/// let config = Config {
///     url: "https://example.com/file.zip".to_string(),
///     saveas: Some("./downloads/file.zip".to_string()),
///     overwrite: Some(true),
///     // Other configuration fields...
/// };
///
/// match create(&env, context, config) {
///     Ok(task) => println!("Task created with ID: {}", task.tid),
///     Err(e) => println!("Error creating task: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn create(env: &AniEnv, context: AniRef, seq: i64) -> Result<String, BusinessError> {
    let context = AniObject::from(context);
    let context = Context::new(env, &context);

    match RequestClient::get_instance().create_task(context, seq as u64) {
        Ok(task_id) => Ok(task_id.to_string()),
        Err(e) => {
            error!("Create task failed: {:?}", e);
            // Handle specific error types and return appropriate business errors
            match e {
                CreateTaskError::DownloadPath(err) => {
                    let (code, message): (i32, String) = match err {
                        DownloadPathError::InvalidPath => (401, "Invalid Path".to_string()),
                        DownloadPathError::EmptyPath => {
                            (13400001, "Invalid file or file system error, File path is empty".to_string())
                        }
                        DownloadPathError::TooLongPath => {
                            (13400001, "Invalid file or file system error, File path exceeds maximum allowed length".to_string())
                        }
                        DownloadPathError::BundleNameNotMap => (
                            13400001,
                            "Invalid file or file system error, File path bundle name does not match the application".to_string(),
                        ),
                        DownloadPathError::AlreadyExists => {
                            (13400001, "Invalid file or file system error, File already exists, set overwrite=true to replace".to_string())
                        }
                        DownloadPathError::CreateFile(e) => {
                            (13400001, format!("Invalid file or file system error, Failed to create file: {}", e))
                        }
                        DownloadPathError::SetPermission(e) => (
                            13400001,
                            format!("Invalid file or file system error, Failed to set file permissions: {}", e),
                        ),
                        DownloadPathError::AclAccess(e) => (
                            13400001,
                            format!("Invalid file or file system error, ACL permission denied: {}", e),
                        ),
                    };
                    Err(BusinessError::new(code, message))
                }
                CreateTaskError::Code(code) => {
                    Err(BusinessError::new_static(code, "Create Task Failed"))
                }
            }
        }
    }
}

/// Retrieves a task by its ID and authentication token.
///
/// # Parameters
///
/// * `context` - The application context
/// * `task_id` - The ID of the task to retrieve
/// * `token` - Optional authentication token
///
/// # Returns
///
/// * `Result<Task, BusinessError>` - Unimplemented
///
/// # Panics
///
/// Panics as this function is unimplemented (`todo!()`).
#[ani_rs::native]
pub fn get_task(context: AniRef, id: String, token: Option<String>) -> Result<Task, BusinessError> {
    let task_id = id.parse::<i64>().map_err(|_| {
        BusinessError::new(
            ExceptionErrorCode::E_TASK_NOT_FOUND as i32,
            "Invalid task ID format".to_string(),
        )
    })?;
    RequestClient::get_instance()
        .get_task(task_id, token)
        .map(|c| Task {
            tid: id,
            config: Config::from(c),
        })
        .map_err(|e| BusinessError::new(e, "Failed to get download task".to_string()))
}

/// Removes a task with the specified ID.
///
/// # Parameters
///
/// * `id` - The ID of the task to remove as a string
///
/// # Returns
///
/// * `Ok(())` if the task was successfully removed
/// * `Err(BusinessError)` if there was an error during removal
///
/// # Errors
///
/// Returns an error if:
/// * The task ID format is invalid
/// * The task removal fails
///
/// # Examples
///
/// ```rust
/// use request_api10::api10::agent::remove;
///
/// match remove("12345".to_string()) {
///     Ok(_) => println!("Task removed successfully"),
///     Err(e) => println!("Error removing task: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn remove(id: String) -> Result<(), BusinessError> {
    let task_id = id.parse::<i64>().map_err(|_| {
        BusinessError::new(
            ExceptionErrorCode::E_TASK_NOT_FOUND as i32,
            "Invalid task ID format".to_string(),
        )
    })?;
    RequestClient::get_instance()
        .remove(task_id)
        .map_err(|e| BusinessError::new_static(e, "Failed to remove task"))?;
    CallbackManager::get_instance().remove_task(task_id);
    Ok(())
}

/// Shows detailed information about a task with the specified ID.
///
/// # Parameters
///
/// * `id` - The ID of the task to show information for
///
/// # Returns
///
/// * `Ok(TaskInfo)` containing the task details
/// * `Err(BusinessError)` if there was an error retrieving the information
///
/// # Errors
///
/// Returns an error if the task information cannot be retrieved.
///
/// # Examples
///
/// ```rust
/// use request_api10::api10::agent::show;
///
/// match show("12345".to_string()) {
///     Ok(info) => println!("Task info: {:?}", info),
///     Err(e) => println!("Error getting task info: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn show(id: String) -> Result<TaskInfo, BusinessError> {
    // Parse string task ID to integer for internal use
    let task_id = id.parse::<i64>().map_err(|_| {
        BusinessError::new(
            ExceptionErrorCode::E_PARAMETER_CHECK as i32,
            "Invalid task ID format".to_string(),
        )
    })?;
    RequestClient::get_instance()
        .show_task(task_id)
        .map(|info| {
            info!("Api10 get task info: {:?}", info);
            TaskInfo::from(info)
        })
        .map_err(|e| BusinessError::new(e, "Failed to get download task info".to_string()))
}

/// Touches a task with the specified ID and authentication token.
///
/// Performs an operation to update the task's last access time or status.
///
/// # Parameters
///
/// * `id` - The ID of the task to touch
/// * `token` - Authentication token
///
/// # Returns
///
/// * `Ok(())` unconditionally (placeholder implementation)
#[ani_rs::native]
pub fn touch(id: String, token: String) -> Result<TaskInfo, BusinessError> {
    let task_id = id.parse::<i64>().map_err(|_| {
        BusinessError::new(
            ExceptionErrorCode::E_TASK_NOT_FOUND as i32,
            "task not found error".to_string(),
        )
    })?;
    RequestClient::get_instance()
        .touch(task_id, token)
        .map(|info| {
            info!("Api10 touch task info: {:?}", info);
            TaskInfo::from(info)
        })
        .map_err(|e| BusinessError::new_static(e, "Failed to touch task"))
}

/// Searches for tasks matching the given filter criteria.
///
/// # Parameters
///
/// * `filter` - Optional filter criteria for the search
///
/// # Returns
///
/// * `Ok(Vec<String>)` containing the IDs of matching tasks
/// * `Err(BusinessError)` if there was an error during the search
///
/// # Errors
///
/// Returns an error if the search operation fails.
///
/// # Examples
///
/// ```rust
/// use request_api10::api10::agent::search;
/// use request_api10::api10::bridge::Filter;
///
/// // Search with no filter (find all tasks)
/// match search(None) {
///     Ok(task_ids) => println!("Found {} tasks", task_ids.len()),
///     Err(e) => println!("Error searching tasks: {}", e),
/// }
///
/// // Search with specific filter
/// let filter = Filter {
///     // Set filter criteria
/// };
/// match search(Some(filter)) {
///     Ok(task_ids) => println!("Found {} matching tasks", task_ids.len()),
///     Err(e) => println!("Error searching tasks: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn search(filter: Option<Filter>) -> Result<Vec<String>, BusinessError> {
    // Convert API filter to core filter, or create empty filter if none provided
    let filter = match filter {
        Some(f) => f.into(),
        None => SearchFilter::new(),
    };
    RequestClient::get_instance()
        .search(filter)
        .map(|tasks| {
            info!("Api10 search tasks: {:?}", tasks);
            tasks
        })
        .map_err(|e| BusinessError::new(e, "Failed to search tasks".to_string()))
}

/// Queries a task with the specified ID.
///
/// # Parameters
///
/// * `id` - The ID of the task to query
///
/// # Returns
///
/// * `Result<TaskInfo, BusinessError>` - Unimplemented
///
/// # Panics
///
/// Panics as this function is unimplemented (`todo!()`).
#[ani_rs::native]
pub fn query(id: String) -> Result<TaskInfo, BusinessError> {
    let task_id = id.parse::<i64>().map_err(|_| {
        BusinessError::new(
            ExceptionErrorCode::E_PARAMETER_CHECK as i32,
            "Invalid task ID format".to_string(),
        )
    })?;
    RequestClient::get_instance()
        .query(task_id)
        .map(|info| {
            info!("Api10 query task info: {:?}", info);
            TaskInfo::from(info)
        })
        .map_err(|e| BusinessError::new_static(e, "Failed to query task"))
}