use crate::app_runtime::AppHandle;
use athas_tooling::{
   LanguageToolConfigSet, LanguageToolStatus, ToolInstaller, ToolRegistry, ToolStatus, ToolType,
};
use serde_json::Value;

#[tauri::command]
pub fn frontend_trace(level: String, scope: String, message: String, payload: Option<Value>) {
   let payload_str = if scope.starts_with("bench:") {
      format_benchmark_payload(payload.as_ref())
   } else {
      format_payload(payload.as_ref())
   };
   match level.as_str() {
      "debug" => log::debug!("[frontend:{}] {}{}", scope, message, payload_str),
      "warn" => log::warn!("[frontend:{}] {}{}", scope, message, payload_str),
      "error" => log::error!("[frontend:{}] {}{}", scope, message, payload_str),
      _ => log::info!("[frontend:{}] {}{}", scope, message, payload_str),
   }
}

fn format_payload(payload: Option<&Value>) -> String {
   let Some(payload) = payload else {
      return String::new();
   };

   match payload {
      Value::Object(map) if map.is_empty() => String::new(),
      Value::Object(map) => {
         let pairs = map
            .iter()
            .map(|(key, value)| format!("{}={}", key, format_value(value)))
            .collect::<Vec<_>>()
            .join(" ");
         format!(" {}", pairs)
      }
      other => format!(" {}", format_value(other)),
   }
}

fn format_benchmark_payload(payload: Option<&Value>) -> String {
   let Some(Value::Object(map)) = payload else {
      return format_payload(payload);
   };

   let total = map
      .get("totalMs")
      .map(format_value)
      .map(|value| format!(" total={}ms", value))
      .unwrap_or_default();

   let duration = map
      .get("durationMs")
      .map(format_value)
      .map(|value| format!(" duration={}ms", value))
      .unwrap_or_default();

   let phases = match map.get("phases") {
      Some(Value::Array(items)) if !items.is_empty() => {
         let formatted = items
            .iter()
            .filter_map(|item| match item {
               Value::Object(phase) => {
                  let label = phase.get("label").and_then(Value::as_str)?;
                  let duration = phase.get("durationMs").map(format_value)?;
                  let detail = phase
                     .get("detail")
                     .and_then(|value| (!value.is_null()).then(|| format_value(value)));
                  Some(match detail {
                     Some(detail) => format!("{}={}ms({})", label, duration, detail),
                     None => format!("{}={}ms", label, duration),
                  })
               }
               _ => None,
            })
            .collect::<Vec<_>>()
            .join(" > ");
         format!(" phases={}", formatted)
      }
      _ => String::new(),
   };

   format!("{}{}{}", total, duration, phases)
}

fn format_value(value: &Value) -> String {
   match value {
      Value::Null => "null".to_string(),
      Value::Bool(boolean) => boolean.to_string(),
      Value::Number(number) => number.to_string(),
      Value::String(text) => text.clone(),
      Value::Array(items) => {
         let formatted = items.iter().map(format_value).collect::<Vec<_>>().join(",");
         format!("[{}]", formatted)
      }
      Value::Object(map) => {
         let formatted = map
            .iter()
            .map(|(key, value)| format!("{}={}", key, format_value(value)))
            .collect::<Vec<_>>()
            .join(",");
         format!("{{{}}}", formatted)
      }
   }
}

/// Install all tools for a language
#[tauri::command]
pub async fn install_language_tools(
   app_handle: AppHandle,
   language_id: String,
   tools: Option<LanguageToolConfigSet>,
) -> Result<LanguageToolStatus, String> {
   let mut status = LanguageToolStatus::new(&language_id);

   let Some(resolved_tools) = ToolRegistry::get_tools(&language_id, tools) else {
      return Ok(status);
   };

   // Install LSP
   if let Some(config) = resolved_tools.get(&ToolType::Lsp) {
      status.lsp = Some(match ToolInstaller::install(&app_handle, config).await {
         Ok(_) => ToolStatus::Installed,
         Err(e) => ToolStatus::Failed(e.to_string()),
      });
   }

   // Install formatter
   if let Some(config) = resolved_tools.get(&ToolType::Formatter) {
      status.formatter = Some(match ToolInstaller::install(&app_handle, config).await {
         Ok(_) => ToolStatus::Installed,
         Err(e) => ToolStatus::Failed(e.to_string()),
      });
   }

   // Install linter
   if let Some(config) = resolved_tools.get(&ToolType::Linter) {
      status.linter = Some(match ToolInstaller::install(&app_handle, config).await {
         Ok(_) => ToolStatus::Installed,
         Err(e) => ToolStatus::Failed(e.to_string()),
      });
   }

   Ok(status)
}

/// Install a specific tool type for a language
#[tauri::command]
pub async fn install_tool(
   app_handle: AppHandle,
   language_id: String,
   tool_type: String,
   tools: Option<LanguageToolConfigSet>,
) -> Result<ToolStatus, String> {
   let tool_type = match tool_type.as_str() {
      "lsp" => ToolType::Lsp,
      "formatter" => ToolType::Formatter,
      "linter" => ToolType::Linter,
      _ => return Err(format!("Unknown tool type: {}", tool_type)),
   };

   let config = ToolRegistry::get_tool(&language_id, tool_type, tools).ok_or_else(|| {
      format!(
         "No {} configured for {}",
         tool_type_str(&tool_type),
         language_id
      )
   })?;

   match ToolInstaller::install(&app_handle, &config).await {
      Ok(_) => Ok(ToolStatus::Installed),
      Err(e) => Ok(ToolStatus::Failed(e.to_string())),
   }
}

/// Get the status of all tools for a language
#[tauri::command]
pub async fn get_language_tool_status(
   app_handle: AppHandle,
   language_id: String,
   tools: Option<LanguageToolConfigSet>,
) -> Result<LanguageToolStatus, String> {
   let mut status = LanguageToolStatus::new(&language_id);

   let Some(resolved_tools) = ToolRegistry::get_tools(&language_id, tools) else {
      return Ok(status);
   };

   // Check LSP
   if let Some(config) = resolved_tools.get(&ToolType::Lsp) {
      status.lsp = Some(
         if ToolInstaller::is_installed(&app_handle, config).unwrap_or(false) {
            ToolStatus::Installed
         } else {
            ToolStatus::NotInstalled
         },
      );
   }

   // Check formatter
   if let Some(config) = resolved_tools.get(&ToolType::Formatter) {
      status.formatter = Some(
         if ToolInstaller::is_installed(&app_handle, config).unwrap_or(false) {
            ToolStatus::Installed
         } else {
            ToolStatus::NotInstalled
         },
      );
   }

   // Check linter
   if let Some(config) = resolved_tools.get(&ToolType::Linter) {
      status.linter = Some(
         if ToolInstaller::is_installed(&app_handle, config).unwrap_or(false) {
            ToolStatus::Installed
         } else {
            ToolStatus::NotInstalled
         },
      );
   }

   Ok(status)
}

/// Get the path to a tool's binary
#[tauri::command]
pub async fn get_tool_path(
   app_handle: AppHandle,
   language_id: String,
   tool_type: String,
   tools: Option<LanguageToolConfigSet>,
) -> Result<Option<String>, String> {
   let tool_type = match tool_type.as_str() {
      "lsp" => ToolType::Lsp,
      "formatter" => ToolType::Formatter,
      "linter" => ToolType::Linter,
      _ => return Err(format!("Unknown tool type: {}", tool_type)),
   };

   let config = match ToolRegistry::get_tool(&language_id, tool_type, tools) {
      Some(c) => c,
      None => return Ok(None),
   };

   let path = match tool_type {
      ToolType::Lsp => {
         ToolInstaller::get_lsp_launch_path(&app_handle, &config).map_err(|e| e.to_string())?
      }
      _ => ToolInstaller::get_tool_path(&app_handle, &config).map_err(|e| e.to_string())?,
   };

   if path.exists() {
      Ok(Some(path.to_string_lossy().to_string()))
   } else {
      Ok(None)
   }
}

/// Get available tools for a language
#[tauri::command]
pub fn get_available_tools(
   language_id: String,
   tools: Option<LanguageToolConfigSet>,
) -> Result<Vec<String>, String> {
   let tools = ToolRegistry::get_tools(&language_id, tools);
   match tools {
      Some(t) => Ok(t.keys().map(|k| tool_type_str(k).to_string()).collect()),
      None => Ok(vec![]),
   }
}

fn tool_type_str(t: &ToolType) -> &'static str {
   match t {
      ToolType::Lsp => "lsp",
      ToolType::Formatter => "formatter",
      ToolType::Linter => "linter",
   }
}
