use std::fs;
use std::path::Path;

use reqwest::Client;
use serde::de::DeserializeOwned;
use serde_json::{Value, json};
use tauri::State;

use super::http_api::get_process_list;
use super::rclone_core::{RCLONE_API_BASE, RCLONE_AUTH};
use crate::conf::rclone::{RcloneCreateRemoteRequest, RcloneMountRequest, RcloneWebdavConfig};
use crate::object::structs::{
    AppState, RcloneMountInfo, RcloneMountListResponse, RcloneRemoteListResponse,
};
use crate::utils::api::{CreateProcessResponse, ProcessConfig, get_api_key, get_server_port};
use crate::utils::args::split_args_vec;
use crate::utils::path::{get_app_logs_dir, get_rclone_binary_path};

struct RcloneApi {
    client: Client,
}

impl RcloneApi {
    fn new() -> Self {
        Self {
            client: Client::new(),
        }
    }

    async fn post_json<T: DeserializeOwned>(
        &self,
        endpoint: &str,
        body: Option<Value>,
    ) -> Result<T, String> {
        let url = format!("{RCLONE_API_BASE}/{endpoint}");
        let mut req = self.client.post(&url).header("Authorization", RCLONE_AUTH);
        if let Some(b) = body {
            req = req.json(&b).header("Content-Type", "application/json");
        }
        let resp = req
            .send()
            .await
            .map_err(|e| format!("Request failed: {e}"))?;
        let status = resp.status();
        if status.is_success() {
            resp.json::<T>()
                .await
                .map_err(|e| format!("Failed to parse JSON: {e}"))
        } else {
            let txt = resp.text().await.unwrap_or_default();
            Err(format!("API error {status}: {txt}"))
        }
    }

    async fn post_text(&self, endpoint: &str) -> Result<String, String> {
        let url = format!("{RCLONE_API_BASE}/{endpoint}");
        let resp = self
            .client
            .post(&url)
            .header("Authorization", RCLONE_AUTH)
            .send()
            .await
            .map_err(|e| format!("Request failed: {e}"))?;
        let status = resp.status();
        if status.is_success() {
            resp.text()
                .await
                .map_err(|e| format!("Failed to read text: {e}"))
        } else {
            let txt = resp.text().await.unwrap_or_default();
            Err(format!("API error {status}: {txt}"))
        }
    }
}

#[tauri::command]
pub async fn rclone_list_config(
    remote_type: String,
    _state: State<'_, AppState>,
) -> Result<Value, String> {
    let api = RcloneApi::new();
    let text = api.post_text("config/dump").await?;
    let all: Value = serde_json::from_str(&text).map_err(|e| format!("Invalid JSON: {e}"))?;
    let remotes = match (remote_type.as_str(), all.as_object()) {
        ("", _) => all.clone(),
        (t, Some(map)) => {
            let filtered = map
                .iter()
                .filter_map(|(name, cfg)| {
                    cfg.get("type")
                        .and_then(Value::as_str)
                        .filter(|&ty| ty == t)
                        .map(|_| (name.clone(), cfg.clone()))
                })
                .collect();
            Value::Object(filtered)
        }
        _ => Value::Object(Default::default()),
    };
    Ok(remotes)
}

#[tauri::command]
pub async fn rclone_list_remotes() -> Result<Vec<String>, String> {
    let api = RcloneApi::new();
    let resp: RcloneRemoteListResponse = api.post_json("config/listremotes", None).await?;
    Ok(resp.remotes)
}

#[tauri::command]
pub async fn rclone_list_mounts() -> Result<RcloneMountListResponse, String> {
    let api = RcloneApi::new();
    api.post_json("mount/listmounts", None).await
}

#[tauri::command]
pub async fn rclone_create_remote(
    name: String,
    r#type: String,
    config: RcloneWebdavConfig,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let api = RcloneApi::new();
    let req = RcloneCreateRemoteRequest {
        name,
        r#type,
        parameters: config,
    };
    api.post_json::<Value>("config/create", Some(json!(req)))
        .await
        .map(|_| true)
}

#[tauri::command]
pub async fn rclone_update_remote(
    name: String,
    r#type: String,
    config: RcloneWebdavConfig,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let api = RcloneApi::new();
    let body = json!({ "name": name, "type": r#type, "parameters": config });
    api.post_json::<Value>("config/update", Some(body))
        .await
        .map(|_| true)
}

#[tauri::command]
pub async fn rclone_delete_remote(
    name: String,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let api = RcloneApi::new();
    let body = json!({ "name": name });
    api.post_json::<Value>("config/delete", Some(body))
        .await
        .map(|_| true)
}

#[tauri::command]
pub async fn rclone_mount_remote(
    mount_request: RcloneMountRequest,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let api = RcloneApi::new();
    api.post_json::<Value>("mount/mount", Some(json!(mount_request)))
        .await
        .map(|_| true)
}

#[tauri::command]
pub async fn rclone_unmount_remote(
    mount_point: String,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let api = RcloneApi::new();
    api.post_json::<Value>("mount/unmount", Some(json!({ "mountPoint": mount_point })))
        .await
        .map(|_| true)
}

#[tauri::command]
pub async fn create_rclone_mount_remote_process(
    config: ProcessConfig,
    _state: State<'_, AppState>,
) -> Result<ProcessConfig, String> {
    let binary_path =
        get_rclone_binary_path().map_err(|e| format!("Failed to get rclone binary path: {e}"))?;
    let log_file_path =
        get_app_logs_dir().map_err(|e| format!("Failed to get app logs directory: {e}"))?;
    let log_file_path = log_file_path.join("process_rclone.log");
    let rclone_conf_path = binary_path
        .parent()
        .map(|p| p.join("rclone.conf"))
        .ok_or_else(|| "Failed to determine rclone.conf path".to_string())?;

    let api_key = get_api_key();
    let port = get_server_port();
    let mut args: Vec<String> = vec![
        "mount".into(),
        "--config".into(),
        rclone_conf_path.to_string_lossy().into_owned(),
    ];
    args.extend(split_args_vec(config.args.clone()));

    let config = ProcessConfig {
        id: config.id.clone(),
        name: config.name.clone(),
        bin_path: binary_path.to_string_lossy().into_owned(),
        args,
        log_file: log_file_path.to_string_lossy().into_owned(),
        working_dir: binary_path
            .parent()
            .map(|p| p.to_string_lossy().into_owned()),
        env_vars: config.env_vars.clone(),
        auto_restart: true,
        auto_start: config.auto_start,
        run_as_admin: false,
        created_at: 0,
        updated_at: 0,
    };
    let client = reqwest::Client::new();
    let response = client
        .post(format!("http://127.0.0.1:{port}/api/v1/processes"))
        .json(&config)
        .header("Authorization", format!("Bearer {api_key}"))
        .send()
        .await
        .map_err(|e| format!("Failed to send request: {e}"))?;
    if response.status().is_success() {
        let response_text = response
            .text()
            .await
            .map_err(|e| format!("Failed to read response text: {e}"))?;
        let process_config = match serde_json::from_str::<CreateProcessResponse>(&response_text) {
            Ok(process_config) => process_config,
            Err(e) => {
                return Err(format!(
                    "Failed to parse response: {e}, response: {response_text}"
                ));
            }
        };
        Ok(process_config.data)
    } else {
        Err(format!(
            "Failed to create Rclone Mount Remote process: {}",
            response.status()
        ))
    }
}

#[tauri::command]
pub async fn check_mount_status(
    mount_point: String,
    _state: State<'_, AppState>,
) -> Result<bool, String> {
    let path = Path::new(&mount_point);
    if !path.exists() {
        return Ok(false);
    }
    #[cfg(target_os = "windows")]
    {
        if mount_point.len() == 2 && mount_point.ends_with(':') {
            let drive_path = format!("{mount_point}\\");
            match fs::read_dir(&drive_path) {
                Ok(_) => return Ok(true),
                Err(_) => return Ok(false),
            }
        }
        match fs::read_dir(&mount_point) {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }
    #[cfg(any(target_os = "linux", target_os = "macos"))]
    {
        match fs::read_dir(&mount_point) {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }
}

#[tauri::command]
pub async fn get_mount_info_list(
    state: State<'_, AppState>,
) -> Result<Vec<RcloneMountInfo>, String> {
    let process_list = get_process_list(state.clone()).await?;
    let mut mount_infos = Vec::new();

    for process in process_list {
        if process.name.starts_with("rclone_mount_") {
            let args = &process.config.args;

            if args.len() >= 3 && args[0] == "mount" {
                let remote_path = args[3].clone();
                let mount_point = args[4].clone();

                let mount_status =
                    match check_mount_status(mount_point.clone(), state.clone()).await {
                        Ok(is_mounted) => {
                            if process.is_running {
                                if is_mounted { "mounted" } else { "mounting" }
                            } else if is_mounted {
                                "unmounting"
                            } else {
                                "unmounted"
                            }
                        }
                        Err(_) => "error",
                    };

                mount_infos.push(RcloneMountInfo {
                    name: remote_path.split(':').next().unwrap_or("").to_string(),
                    process_id: process.id,
                    remote_path,
                    mount_point,
                    status: mount_status.to_string(),
                });
            }
        }
    }
    Ok(mount_infos)
}
