#![allow(dead_code)]
use log;
use reqwest::{
    multipart::{Form, Part},
    Client, ClientBuilder,
};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use std::time::Duration;
use urlencoding;

/// Основная ошибка библиотеки
#[derive(Debug, thiserror::Error)]
pub enum SynologyError {
    #[error("HTTP error: {0}")]
    Http(#[from] reqwest::Error),

    #[error("API error: code {code} - {message:?}")]
    Api { code: i64, message: Option<String> },

    #[error("Invalid response format: {0}")]
    InvalidResponseFormat(String),

    #[error("Login required")]
    LoginRequired,

    #[error("Not logged in")]
    NotLoggedIn,

    #[error("Invalid parameter: {0}")]
    InvalidParameter(String),

    #[error("MIME error: {0}")]
    Mime(#[from] reqwest::header::InvalidHeaderValue),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("CSV error: {0}")]
    Csv(String),
}

/// Результат операций
pub type Result<T> = std::result::Result<T, SynologyError>;

/// Базовый ответ API
#[derive(Debug, Deserialize)]
struct ApiResponse<T> {
    success: bool,
    #[serde(default)]
    data: Option<T>,
    #[serde(default)]
    error: Option<ApiError>,
}

/// Пустой тип для ответов без данных
#[derive(Debug, Default, Deserialize)]
struct EmptyData;

#[derive(Debug, Deserialize)]
struct ApiError {
    code: i64,
    #[serde(default)]
    #[allow(dead_code)]
    errors: Option<Vec<Value>>,
}

/// Информация о доступных API
#[derive(Debug, Clone, Default, Deserialize)]
pub struct ApiInfo {
    pub path: String,
    pub min_version: i32,
    pub max_version: i32,
}

/// Информация о виртуальных файловых системах
#[derive(Debug, Default, Deserialize)]
pub struct VirtualSupport {
    #[serde(default)]
    pub enable_iso_mount: bool,
    #[serde(default)]
    pub enable_remote_mount: bool,
}

/// Информация о File Station
#[derive(Debug, Default, Deserialize)]
pub struct FileStationInfo {
    pub is_manager: bool,
    pub hostname: String,
    pub support_sharing: bool,
    #[serde(default)]
    pub allow_heic_original: bool,
    #[serde(default)]
    pub allow_normal_disable_html: bool,
    #[serde(default)]
    pub enable_list_usergrp: bool,
    #[serde(default)]
    pub enable_send_email_attachment: bool,
    #[serde(default)]
    pub enable_view_google: bool,
    #[serde(default)]
    pub enable_view_microsoft: bool,
    #[serde(default)]
    pub support_file_request: bool,
    #[serde(default)]
    pub support_vfs: bool,
    #[serde(default)]
    pub support_virtual: Option<VirtualSupport>,
    #[serde(default)]
    pub support_virtual_protocol: Vec<String>,
    #[serde(default)]
    pub system_codepage: String,
    #[serde(default)]
    pub uid: u64,
}

/// Объект файла/папки
#[derive(Debug, Clone, Default, Deserialize)]
pub struct FileInfo {
    pub path: String,
    pub name: String,
    pub isdir: bool,
    #[serde(default)]
    pub additional: Option<FileAdditional>,
}

/// Дополнительная информация о файле
#[derive(Debug, Clone, Default, Deserialize)]
pub struct FileAdditional {
    pub real_path: Option<String>,
    pub size: Option<u64>,
    #[serde(default)]
    pub owner: Option<OwnerInfo>,
    #[serde(default)]
    pub time: Option<TimeInfo>,
    #[serde(default)]
    pub perm: Option<PermInfo>,
    pub r#type: Option<String>,
}

/// Информация о владельце
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OwnerInfo {
    pub user: String,
    pub group: String,
    pub uid: u32,
    pub gid: u32,
}

/// Информация о времени
#[derive(Debug, Clone, Default, Deserialize)]
pub struct TimeInfo {
    pub atime: u64,
    pub mtime: u64,
    pub ctime: u64,
    pub crtime: u64,
}

/// Информация о правах доступа
#[derive(Debug, Clone, Default, Deserialize)]
pub struct PermInfo {
    pub posix: u32,
    pub is_acl_mode: bool,
    #[serde(default)]
    pub acl: Option<AclInfo>,
}

#[derive(Debug, Clone, Default, Deserialize)]
pub struct AclInfo {
    pub append: bool,
    pub del: bool,
    pub exec: bool,
    pub read: bool,
    pub write: bool,
}

/// Общая папка
#[derive(Debug, Default, Deserialize)]
pub struct Share {
    pub path: String,
    pub name: String,
    #[serde(default)]
    pub additional: Option<FileAdditional>,
}

/// Список файлов
#[derive(Debug, Default, Deserialize)]
pub struct FileList {
    pub total: i32,
    pub offset: i32,
    pub files: Vec<FileInfo>,
}

/// Список общих папок
#[derive(Debug, Default, Deserialize)]
pub struct ShareList {
    pub total: i32,
    pub offset: i32,
    pub shares: Vec<Share>,
}

/// Информация о задаче (для асинхронных операций)
#[derive(Debug, Default, Deserialize)]
pub struct TaskInfo {
    pub taskid: String,
}

/// Статус задачи
#[derive(Debug, Default, Deserialize)]
pub struct TaskStatus {
    pub finished: bool,
    #[serde(default)]
    pub progress: Option<f64>,
    #[serde(default)]
    pub total: Option<i64>,
    #[serde(default)]
    pub processed_size: Option<u64>,
    #[serde(default)]
    pub processed_num: Option<u32>,
    #[serde(default)]
    pub path: Option<String>,
    #[serde(default)]
    pub processing_path: Option<String>,
    #[serde(default)]
    pub dest_folder_path: Option<String>,
}

/// Результат поиска
#[derive(Debug, Default, Deserialize)]
pub struct SearchResult {
    pub total: i32,
    pub offset: i32,
    pub finished: bool,
    pub files: Vec<FileInfo>,
}

/// Информация о шаринге
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SharingLink {
    pub id: String,
    pub url: String,
    pub link_owner: String,
    pub path: String,
    pub is_folder: bool,
    pub has_password: bool,
    pub date_expired: String,
    pub date_available: String,
    pub status: String,
}

/// Созданная ссылка
#[derive(Debug, Clone, Default, Deserialize)]
pub struct CreatedLink {
    pub path: String,
    pub url: String,
    pub id: String,
    pub qrcode: Option<String>,
    pub error: i32,
}

/// Параметры сортировки
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortBy {
    Name,
    Size,
    User,
    Group,
    MTime,
    ATime,
    CTime,
    Crtime,
    Posix,
    Type,
}

impl SortBy {
    fn as_str(&self) -> &'static str {
        match self {
            SortBy::Name => "name",
            SortBy::Size => "size",
            SortBy::User => "user",
            SortBy::Group => "group",
            SortBy::MTime => "mtime",
            SortBy::ATime => "atime",
            SortBy::CTime => "ctime",
            SortBy::Crtime => "crtime",
            SortBy::Posix => "posix",
            SortBy::Type => "type",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortDirection {
    Asc,
    Desc,
}

impl SortDirection {
    fn as_str(&self) -> &'static str {
        match self {
            SortDirection::Asc => "asc",
            SortDirection::Desc => "desc",
        }
    }
}

/// Параметры для list_files
#[derive(Default)]
pub struct ListFilesOptions {
    pub offset: Option<i32>,
    pub limit: Option<i32>,
    pub sort_by: Option<SortBy>,
    pub sort_direction: Option<SortDirection>,
    pub pattern: Option<String>,
    pub file_type: Option<String>,
    pub additional: Option<Vec<String>>,
}

impl ListFilesOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn offset(mut self, offset: i32) -> Self {
        self.offset = Some(offset);
        self
    }

    pub fn limit(mut self, limit: i32) -> Self {
        self.limit = Some(limit);
        self
    }

    pub fn sort_by(mut self, sort_by: SortBy) -> Self {
        self.sort_by = Some(sort_by);
        self
    }

    pub fn sort_direction(mut self, sort_direction: SortDirection) -> Self {
        self.sort_direction = Some(sort_direction);
        self
    }

    pub fn pattern(mut self, pattern: &str) -> Self {
        self.pattern = Some(pattern.to_string());
        self
    }

    pub fn file_type(mut self, file_type: &str) -> Self {
        self.file_type = Some(file_type.to_string());
        self
    }

    pub fn additional(mut self, additional: &[&str]) -> Self {
        self.additional = Some(additional.iter().map(|&s| s.to_string()).collect());
        self
    }
}

/// Параметры для list_shares
#[derive(Default)]
pub struct ListSharesOptions {
    pub offset: Option<i32>,
    pub limit: Option<i32>,
    pub sort_by: Option<SortBy>,
    pub sort_direction: Option<SortDirection>,
    pub only_writable: Option<bool>,
    pub additional: Option<Vec<String>>,
}

impl ListSharesOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn offset(mut self, offset: i32) -> Self {
        self.offset = Some(offset);
        self
    }

    pub fn limit(mut self, limit: i32) -> Self {
        self.limit = Some(limit);
        self
    }

    pub fn sort_by(mut self, sort_by: SortBy) -> Self {
        self.sort_by = Some(sort_by);
        self
    }

    pub fn sort_direction(mut self, sort_direction: SortDirection) -> Self {
        self.sort_direction = Some(sort_direction);
        self
    }

    pub fn only_writable(mut self, only_writable: bool) -> Self {
        self.only_writable = Some(only_writable);
        self
    }

    pub fn additional(mut self, additional: &[&str]) -> Self {
        self.additional = Some(additional.iter().map(|&s| s.to_string()).collect());
        self
    }
}

/// Клиент для работы с Synology API
pub struct SynologyClient {
    client: Client,
    base_url: String,
    sid: Option<String>,
    session_name: String,
}

impl SynologyClient {
    /// Создание нового клиента
    pub fn new(base_url: &str) -> Self {
        let client = ClientBuilder::new()
            .timeout(Duration::from_secs(30))
            .build()
            .expect("Failed to create HTTP client");

        Self {
            client,
            base_url: base_url.trim_end_matches('/').to_string(),
            sid: None,
            session_name: "FileStation".to_string(),
        }
    }

    /// Установка имени сессии
    pub fn with_session_name(mut self, name: &str) -> Self {
        self.session_name = name.to_string();
        self
    }

    /// Проверка авторизации
    pub fn is_authenticated(&self) -> bool {
        self.sid.is_some()
    }

    /// Выполнение запроса к API (с возвратом данных)
    async fn request<T: DeserializeOwned + Default>(
        &self,
        path: &str,
        params: &[(&str, String)],
    ) -> Result<T> {
        let mut url = format!("{}/webapi/{}", self.base_url, path);

        let mut param_parts: Vec<String> = Vec::new();
        for (key, value) in params {
            param_parts.push(format!("{}={}", key, urlencoding::encode(value)));
        }
        if !param_parts.is_empty() {
            url = format!("{}?{}", url, param_parts.join("&"));
        }

        log::debug!("Request URL: {}", url);

        let response = self.client.get(&url).send().await?;

        let status = response.status();
        if !status.is_success() {
            let error_text = response.text().await?;
            log::debug!("Error response: {}", error_text);
            return Err(SynologyError::Api {
                code: status.as_u16() as i64,
                message: Some(error_text),
            });
        }

        let response_text = response.text().await?;
        log::debug!("Response: {}", response_text);

        let api_response: ApiResponse<T> = serde_json::from_str(&response_text)
            .map_err(|e| SynologyError::InvalidResponseFormat(e.to_string()))?;

        if api_response.success {
            if std::any::type_name::<T>() == std::any::type_name::<EmptyData>() {
                Ok(T::default())
            } else {
                api_response.data.ok_or_else(|| {
                    SynologyError::InvalidResponseFormat("Missing data field".to_string())
                })
            }
        } else {
            let code = api_response.error.as_ref().map(|e| e.code).unwrap_or(-1);
            Err(SynologyError::Api {
                code,
                message: None,
            })
        }
    }
    
    /// Выполнение запроса с SID
    async fn request_auth<T: DeserializeOwned + Default>(
        &self,
        path: &str,
        params: Vec<(&str, String)>,
    ) -> Result<T> {
        let mut all_params = params;
        if let Some(sid) = &self.sid {
            all_params.push(("_sid", sid.clone()));
        }
        self.request(path, &all_params).await
    }

    /// Выполнение запроса без возврата данных (только проверка success)
    async fn request_auth_void(&self, path: &str, params: Vec<(&str, String)>) -> Result<()> {
        let _: EmptyData = self.request_auth(path, params).await?;
        Ok(())
    }

    /// Получение информации о доступных API
    pub async fn get_api_info(&self, apis: &[&str]) -> Result<HashMap<String, ApiInfo>> {
        let query = if apis.is_empty() {
            "all".to_string()
        } else {
            apis.join(",")
        };

        let response: Value = self
            .request(
                "query.cgi",
                &[
                    ("api", "SYNO.API.Info".to_string()),
                    ("version", "1".to_string()),
                    ("method", "query".to_string()),
                    ("query", query),
                ],
            )
            .await?;

        let result: HashMap<String, ApiInfo> = serde_json::from_value(response)
            .map_err(|e| SynologyError::InvalidResponseFormat(e.to_string()))?;

        Ok(result)
    }

    /// Вход в систему
    pub async fn login(&mut self, account: &str, password: &str) -> Result<String> {
        let response: HashMap<String, String> = self
            .request(
                "auth.cgi",
                &[
                    ("api", "SYNO.API.Auth".to_string()),
                    ("version", "3".to_string()),
                    ("method", "login".to_string()),
                    ("account", account.to_string()),
                    ("passwd", password.to_string()),
                    ("session", self.session_name.clone()),
                    ("format", "sid".to_string()),
                ],
            )
            .await?;

        let sid = response
            .get("sid")
            .ok_or_else(|| {
                SynologyError::InvalidResponseFormat("Missing sid in response".to_string())
            })?
            .clone();

        self.sid = Some(sid.clone());
        Ok(sid)
    }

    /// Выход из системы
    pub async fn logout(&mut self) -> Result<()> {
        if self.sid.is_none() {
            return Ok(());
        }

        self.request_auth_void(
            "auth.cgi",
            vec![
                ("api", "SYNO.API.Auth".to_string()),
                ("version", "1".to_string()),
                ("method", "logout".to_string()),
                ("session", self.session_name.clone()),
            ],
        )
        .await?;

        self.sid = None;
        Ok(())
    }

    /// Получение информации о File Station
    pub async fn get_info(&self) -> Result<FileStationInfo> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Info".to_string()),
                ("version", "2".to_string()),
                ("method", "get".to_string()),
            ],
        )
        .await
    }

    /// Получение списка общих папок
    pub async fn list_shares(&self, options: ListSharesOptions) -> Result<ShareList> {
        let mut params = vec![
            ("api", "SYNO.FileStation.List".to_string()),
            ("version", "2".to_string()),
            ("method", "list_share".to_string()),
        ];

        if let Some(offset) = options.offset {
            params.push(("offset", offset.to_string()));
        }
        if let Some(limit) = options.limit {
            params.push(("limit", limit.to_string()));
        }
        if let Some(sort_by) = options.sort_by {
            params.push(("sort_by", sort_by.as_str().to_string()));
        }
        if let Some(sort_direction) = options.sort_direction {
            params.push(("sort_direction", sort_direction.as_str().to_string()));
        }
        if let Some(only_writable) = options.only_writable {
            params.push(("onlywritable", only_writable.to_string()));
        }
        if let Some(additional) = options.additional {
            let additional_str = additional
                .iter()
                .map(|s| format!("\"{}\"", s))
                .collect::<Vec<_>>()
                .join(",");
            params.push(("additional", format!("[{}]", additional_str)));
        }

        self.request_auth("entry.cgi", params).await
    }

    /// Получение списка файлов в папке
    pub async fn list_files(
        &self,
        folder_path: &str,
        options: ListFilesOptions,
    ) -> Result<FileList> {
        let mut params = vec![
            ("api", "SYNO.FileStation.List".to_string()),
            ("version", "2".to_string()),
            ("method", "list".to_string()),
            ("folder_path", folder_path.to_string()),
        ];

        if let Some(offset) = options.offset {
            params.push(("offset", offset.to_string()));
        }
        if let Some(limit) = options.limit {
            params.push(("limit", limit.to_string()));
        }
        if let Some(sort_by) = options.sort_by {
            params.push(("sort_by", sort_by.as_str().to_string()));
        }
        if let Some(sort_direction) = options.sort_direction {
            params.push(("sort_direction", sort_direction.as_str().to_string()));
        }
        if let Some(pattern) = options.pattern {
            params.push(("pattern", pattern));
        }
        if let Some(file_type) = options.file_type {
            params.push(("filetype", file_type));
        }
        if let Some(additional) = options.additional {
            let additional_str = additional
                .iter()
                .map(|s| format!("\"{}\"", s))
                .collect::<Vec<_>>()
                .join(",");
            params.push(("additional", format!("[{}]", additional_str)));
        }

        self.request_auth("entry.cgi", params).await
    }

    /// Создание папки
    pub async fn create_folder(
        &self,
        folder_path: &str,
        name: &str,
        force_parent: Option<bool>,
    ) -> Result<Vec<FileInfo>> {
        let mut params = vec![
            ("api", "SYNO.FileStation.CreateFolder".to_string()),
            ("version", "2".to_string()),
            ("method", "create".to_string()),
            ("folder_path", format!("[\"{}\"]", folder_path)),
            ("name", format!("[\"{}\"]", name)),
        ];

        if let Some(force_parent) = force_parent {
            params.push(("force_parent", force_parent.to_string()));
        }

        let response: HashMap<String, Vec<FileInfo>> =
            self.request_auth("entry.cgi", params).await?;

        Ok(response.get("folders").cloned().unwrap_or_default())
    }

    /// Переименование файла/папки
    pub async fn rename(&self, path: &str, new_name: &str) -> Result<Vec<FileInfo>> {
        let response: HashMap<String, Vec<FileInfo>> = self
            .request_auth(
                "entry.cgi",
                vec![
                    ("api", "SYNO.FileStation.Rename".to_string()),
                    ("version", "2".to_string()),
                    ("method", "rename".to_string()),
                    ("path", format!("[\"{}\"]", path)),
                    ("name", format!("[\"{}\"]", new_name)),
                ],
            )
            .await?;

        Ok(response.get("files").cloned().unwrap_or_default())
    }

    /// Копирование файлов/папок (асинхронное)
    pub async fn copy_start(
        &self,
        paths: &[&str],
        dest_folder_path: &str,
        overwrite: Option<bool>,
    ) -> Result<String> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.CopyMove".to_string()),
            ("version", "3".to_string()),
            ("method", "start".to_string()),
            ("path", format!("[{}]", paths_str)),
            ("dest_folder_path", dest_folder_path.to_string()),
            ("remove_src", "false".to_string()),
        ];

        if let Some(overwrite) = overwrite {
            params.push(("overwrite", overwrite.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Перемещение файлов/папок (асинхронное)
    pub async fn move_start(
        &self,
        paths: &[&str],
        dest_folder_path: &str,
        overwrite: Option<bool>,
    ) -> Result<String> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.CopyMove".to_string()),
            ("version", "3".to_string()),
            ("method", "start".to_string()),
            ("path", format!("[{}]", paths_str)),
            ("dest_folder_path", dest_folder_path.to_string()),
            ("remove_src", "true".to_string()),
        ];

        if let Some(overwrite) = overwrite {
            params.push(("overwrite", overwrite.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Получение статуса задачи копирования/перемещения
    pub async fn get_task_status(&self, task_id: &str) -> Result<TaskStatus> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.CopyMove".to_string()),
                ("version", "3".to_string()),
                ("method", "status".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await
    }

    /// Удаление файлов/папок (блокирующее)
    pub async fn delete(&self, paths: &[&str], recursive: Option<bool>) -> Result<()> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.Delete".to_string()),
            ("version", "2".to_string()),
            ("method", "delete".to_string()),
            ("path", format!("[{}]", paths_str)),
        ];

        if let Some(recursive) = recursive {
            params.push(("recursive", recursive.to_string()));
        }

        self.request_auth_void("entry.cgi", params).await?;
        Ok(())
    }

    /// Удаление файлов/папок (асинхронное)
    pub async fn delete_start(&self, paths: &[&str], recursive: Option<bool>) -> Result<String> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.Delete".to_string()),
            ("version", "2".to_string()),
            ("method", "start".to_string()),
            ("path", format!("[{}]", paths_str)),
        ];

        if let Some(recursive) = recursive {
            params.push(("recursive", recursive.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Получение статуса удаления
    pub async fn get_delete_status(&self, task_id: &str) -> Result<TaskStatus> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Delete".to_string()),
                ("version", "2".to_string()),
                ("method", "status".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await
    }

    /// Поиск файлов
    pub async fn search_start(
        &self,
        folder_paths: &[&str],
        recursive: Option<bool>,
        pattern: Option<&str>,
        file_type: Option<&str>,
        extension: Option<&str>,
    ) -> Result<String> {
        let paths_str = folder_paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.Search".to_string()),
            ("version", "2".to_string()),
            ("method", "start".to_string()),
            ("folder_path", format!("[{}]", paths_str)),
        ];

        if let Some(recursive) = recursive {
            params.push(("recursive", recursive.to_string()));
        }
        if let Some(pattern) = pattern {
            params.push(("pattern", pattern.to_string()));
        }
        if let Some(file_type) = file_type {
            params.push(("filetype", file_type.to_string()));
        }
        if let Some(extension) = extension {
            params.push(("extension", extension.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Получение результатов поиска
    pub async fn search_list(
        &self,
        task_id: &str,
        offset: Option<i32>,
        limit: Option<i32>,
        additional: Option<&[&str]>,
    ) -> Result<SearchResult> {
        let mut params = vec![
            ("api", "SYNO.FileStation.Search".to_string()),
            ("version", "2".to_string()),
            ("method", "list".to_string()),
            ("taskid", task_id.to_string()),
        ];

        if let Some(offset) = offset {
            params.push(("offset", offset.to_string()));
        }
        if let Some(limit) = limit {
            params.push(("limit", limit.to_string()));
        }
        if let Some(additional) = additional {
            let additional_str = additional
                .iter()
                .map(|&s| format!("\"{}\"", s))
                .collect::<Vec<_>>()
                .join(",");
            params.push(("additional", format!("[{}]", additional_str)));
        }

        self.request_auth("entry.cgi", params).await
    }

    /// Остановка поиска
    pub async fn search_stop(&self, task_id: &str) -> Result<()> {
        self.request_auth_void(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Search".to_string()),
                ("version", "2".to_string()),
                ("method", "stop".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await?;
        Ok(())
    }

    /// Очистка результатов поиска
    pub async fn search_clean(&self, task_id: &str) -> Result<()> {
        self.request_auth_void(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Search".to_string()),
                ("version", "2".to_string()),
                ("method", "clean".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await?;
        Ok(())
    }

    /// Создание ссылки для скачивания
    pub async fn create_sharing_link(
        &self,
        path: &str,
        password: Option<&str>,
        date_expired: Option<&str>,
        date_available: Option<&str>,
    ) -> Result<CreatedLink> {
        let mut params = vec![
            ("api", "SYNO.FileStation.Sharing".to_string()),
            ("version", "3".to_string()),
            ("method", "create".to_string()),
            ("path", path.to_string()),
        ];

        if let Some(password) = password {
            params.push(("password", password.to_string()));
        }
        if let Some(date_expired) = date_expired {
            params.push(("date_expired", date_expired.to_string()));
        }
        if let Some(date_available) = date_available {
            params.push(("date_available", date_available.to_string()));
        }

        let response: HashMap<String, Vec<CreatedLink>> =
            self.request_auth("entry.cgi", params).await?;

        response
            .get("links")
            .and_then(|links| links.first().cloned())
            .ok_or_else(|| {
                SynologyError::InvalidResponseFormat("Missing links in response".to_string())
            })
    }

    /// Получение списка ссылок
    pub async fn list_sharing_links(
        &self,
        offset: Option<i32>,
        limit: Option<i32>,
    ) -> Result<Vec<SharingLink>> {
        let mut params = vec![
            ("api", "SYNO.FileStation.Sharing".to_string()),
            ("version", "3".to_string()),
            ("method", "list".to_string()),
        ];

        if let Some(offset) = offset {
            params.push(("offset", offset.to_string()));
        }
        if let Some(limit) = limit {
            params.push(("limit", limit.to_string()));
        }

        let response: HashMap<String, Value> = self.request_auth("entry.cgi", params).await?;

        let links = response
            .get("links")
            .and_then(|v| serde_json::from_value(v.clone()).ok())
            .unwrap_or_default();

        Ok(links)
    }

    /// Удаление ссылки
    pub async fn delete_sharing_link(&self, id: &str) -> Result<()> {
        self.request_auth_void(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Sharing".to_string()),
                ("version", "3".to_string()),
                ("method", "delete".to_string()),
                ("id", id.to_string()),
            ],
        )
        .await?;
        Ok(())
    }

    /// Получение размера папки (асинхронное)
    pub async fn get_dir_size_start(&self, paths: &[&str]) -> Result<String> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let response: TaskInfo = self
            .request_auth(
                "entry.cgi",
                vec![
                    ("api", "SYNO.FileStation.DirSize".to_string()),
                    ("version", "2".to_string()),
                    ("method", "start".to_string()),
                    ("path", format!("[{}]", paths_str)),
                ],
            )
            .await?;

        Ok(response.taskid)
    }

    /// Получение статуса вычисления размера
    pub async fn get_dir_size_status(&self, task_id: &str) -> Result<TaskStatus> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.DirSize".to_string()),
                ("version", "2".to_string()),
                ("method", "status".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await
    }

    /// Архивирование файлов
    pub async fn compress_start(
        &self,
        paths: &[&str],
        dest_file_path: &str,
        format: Option<&str>,
        password: Option<&str>,
    ) -> Result<String> {
        let paths_str = paths
            .iter()
            .map(|&p| format!("\"{}\"", p))
            .collect::<Vec<_>>()
            .join(",");

        let mut params = vec![
            ("api", "SYNO.FileStation.Compress".to_string()),
            ("version", "3".to_string()),
            ("method", "start".to_string()),
            ("path", format!("[{}]", paths_str)),
            ("dest_file_path", dest_file_path.to_string()),
        ];

        if let Some(format) = format {
            params.push(("format", format.to_string()));
        }
        if let Some(password) = password {
            params.push(("password", password.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Получение статуса архивации
    pub async fn get_compress_status(&self, task_id: &str) -> Result<TaskStatus> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Compress".to_string()),
                ("version", "3".to_string()),
                ("method", "status".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await
    }

    /// Извлечение архива
    pub async fn extract_start(
        &self,
        file_path: &str,
        dest_folder_path: &str,
        overwrite: Option<bool>,
        password: Option<&str>,
    ) -> Result<String> {
        let mut params = vec![
            ("api", "SYNO.FileStation.Extract".to_string()),
            ("version", "2".to_string()),
            ("method", "start".to_string()),
            ("file_path", file_path.to_string()),
            ("dest_folder_path", dest_folder_path.to_string()),
        ];

        if let Some(overwrite) = overwrite {
            params.push(("overwrite", overwrite.to_string()));
        }
        if let Some(password) = password {
            params.push(("password", password.to_string()));
        }

        let response: TaskInfo = self.request_auth("entry.cgi", params).await?;
        Ok(response.taskid)
    }

    /// Получение статуса извлечения
    pub async fn get_extract_status(&self, task_id: &str) -> Result<TaskStatus> {
        self.request_auth(
            "entry.cgi",
            vec![
                ("api", "SYNO.FileStation.Extract".to_string()),
                ("version", "2".to_string()),
                ("method", "status".to_string()),
                ("taskid", task_id.to_string()),
            ],
        )
        .await
    }

    /// Скачивание файла (возвращает бинарные данные)
    pub async fn download(&self, path: &str) -> Result<Vec<u8>> {
        let mut url = format!(
            "{}/webapi/entry.cgi?api=SYNO.FileStation.Download&version=2&method=download&path={}&mode=open",
            self.base_url, path
        );

        if let Some(sid) = &self.sid {
            url = format!("{}&_sid={}", url, sid);
        }

        let response = self.client.get(&url).send().await?;
        let status = response.status();
        if !status.is_success() {
            let error_text = response.text().await?;
            return Err(SynologyError::Api {
                code: status.as_u16() as i64,
                message: Some(error_text),
            });
        }
        Ok(response.bytes().await?.to_vec())
    }

    /// Загрузка файла (multipart)
    pub async fn upload(
        &self,
        folder_path: &str,
        filename: &str,
        data: Vec<u8>,
        create_parents: Option<bool>,
        overwrite: Option<bool>,
    ) -> Result<()> {
        let mut form = Form::new()
            .text("api", "SYNO.FileStation.Upload".to_string())
            .text("version", "2".to_string())
            .text("method", "upload".to_string())
            .text("path", folder_path.to_string());

        if let Some(create_parents) = create_parents {
            form = form.text("create_parents", create_parents.to_string());
        }
        if let Some(overwrite) = overwrite {
            form = form.text("overwrite", overwrite.to_string());
        }

        let file_part = Part::bytes(data)
            .file_name(filename.to_string())
            .mime_str("application/octet-stream")?;

        form = form.part("file", file_part);

        let mut url = format!("{}/webapi/entry.cgi", self.base_url);

        if let Some(sid) = &self.sid {
            url = format!("{}?_sid={}", url, sid);
        }

        let response = self.client.post(&url).multipart(form).send().await?;
        let api_response: ApiResponse<EmptyData> = response.json().await?;

        if api_response.success {
            Ok(())
        } else {
            let code = api_response.error.as_ref().map(|e| e.code).unwrap_or(-1);
            Err(SynologyError::Api {
                code,
                message: None,
            })
        }
    }

    /// Поиск файла по имени и пути
    pub async fn search_file_by_name_and_path(&self, name: &str, path: &str) -> Result<Value> {
        let url = format!("{}/webapi/entry.cgi", self.base_url);
        let data = format!(
            "api=SYNO.SynologyDrive.Files&method=search&version=2&keyword={}&path={}&_sid={}",
            urlencoding::encode(name),
            urlencoding::encode(path),
            self.sid.as_ref().unwrap_or(&String::new())
        );

        log::debug!("Searching for file: {} in path: {}", name, path);
        let response = self
            .client
            .post(&url)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .body(data)
            .send()
            .await?;

        let response_text = response.text().await?;

        let json: Value = serde_json::from_str(&response_text)
            .map_err(|e| SynologyError::InvalidResponseFormat(e.to_string()))?;

        if json.get("success").and_then(|s| s.as_bool()) != Some(true) {
            let code = json["error"]["code"].as_i64().unwrap_or(-1);
            return Err(SynologyError::Api {
                code,
                message: Some(format!("{:?}", json.get("error"))),
            });
        }

        Ok(json)
    }

    /// Поиск файла по имени (без ограничения по пути)
    pub async fn search_file_by_name(&self, name: &str) -> Result<Value> {
        let url = format!("{}/webapi/entry.cgi", self.base_url);
        let data = format!(
            "api=SYNO.SynologyDrive.Files&method=search&version=2&keyword={}&_sid={}",
            urlencoding::encode(name),
            self.sid.as_ref().unwrap_or(&String::new())
        );

        log::debug!("Searching for file: {}", name);

        let response = self
            .client
            .post(&url)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .body(data)
            .send()
            .await?;

        let response_text = response.text().await?;

        let json: Value = serde_json::from_str(&response_text)
            .map_err(|e| SynologyError::InvalidResponseFormat(e.to_string()))?;

        if json.get("success").and_then(|s| s.as_bool()) != Some(true) {
            let code = json["error"]["code"].as_i64().unwrap_or(-1);
            return Err(SynologyError::Api {
                code,
                message: Some(format!("{:?}", json.get("error"))),
            });
        }

        Ok(json)
    }

    /// Экспорт файла Synology Office по ID
    pub async fn export_by_file_id(&self, file_id: &str, file_name: &str) -> Result<Vec<u8>> {
        let export_endpoint = file_name.replace(".osheet", ".xlsx");
        let endpoint = format!("entry.cgi/{}", export_endpoint);

        let params = format!(
            "api=SYNO.Office.Export&method=download&version=1&path=id:{}&_sid={}",
            file_id,
            self.sid.as_ref().unwrap_or(&String::new())
        );

        let url = format!("{}/webapi/{}", self.base_url, endpoint);
        log::debug!("Export URL: {}", url);
        let response = self
            .client
            .post(&url)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .body(params)
            .send()
            .await?;

        let status = response.status();

        if !status.is_success() {
            let error_text = response.text().await?;
            return Err(SynologyError::Api {
                code: status.as_u16() as i64,
                message: Some(error_text),
            });
        }

        let bytes = response.bytes().await?;
        log::debug!("Received {} bytes", bytes.len());
        Ok(bytes.to_vec())
    }
}

impl Drop for SynologyClient {
    fn drop(&mut self) {}
}
