pub mod clipboard;
pub mod config;
pub mod license;

use crate::license::{
  activate_license_command, check_active_license, get_hwid, get_hwid_command,
  is_license_active_command, verify_and_save_offline_token,
};
use shared::cleanser::sanitize_text;

use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;

static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem};
use tauri::tray::TrayIconBuilder;
use tauri::{AppHandle, Manager, State, WebviewUrl, WebviewWindowBuilder};
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
use tauri_plugin_notification::NotificationExt;

use crate::config::{get_config, load_config, save_config};
use shared::types::CleanConfig;

#[cfg(target_os = "windows")]
#[link(name = "user32")]
extern "system" {
  fn MessageBeep(uType: u32) -> i32;
  fn keybd_event(bVk: u8, bScan: u8, dwFlags: u32, dwExtraInfo: usize);
}

#[cfg(target_os = "windows")]
fn simulate_copy() {
  unsafe {
    keybd_event(0x10, 0, 2, 0);
    keybd_event(0x12, 0, 2, 0);
    keybd_event(0x11, 0, 0, 0);
    keybd_event(0x43, 0, 0, 0);
    keybd_event(0x43, 0, 2, 0);
    keybd_event(0x11, 0, 2, 0);
  }
}

#[cfg(not(target_os = "windows"))]
fn simulate_copy() {}

fn is_hotkey_ctrl_c(hotkey_str: &str) -> bool {
  match Shortcut::from_str(hotkey_str) {
    Ok(s) => {
      use tauri_plugin_global_shortcut::{Code, Modifiers};
      s.mods == Modifiers::CONTROL && s.key == Code::KeyC
    }
    Err(_) => false,
  }
}

pub struct ClipboardMonitorState {
  pub shutdown: std::sync::Mutex<Option<clipboard_win::monitor::Shutdown>>,
}

#[cfg(target_os = "windows")]
fn start_clipboard_monitor(app: AppHandle) -> clipboard_win::monitor::Shutdown {
  use std::sync::mpsc;

  let (tx, rx) = mpsc::channel();

  std::thread::spawn(move || {
    let mut monitor = match clipboard_win::Monitor::new() {
      Ok(m) => m,
      Err(e) => {
        log::error!("Failed to create clipboard monitor: {}", e);
        return;
      }
    };

    let shutdown = monitor.shutdown_channel();
    let _ = tx.send(shutdown);

    log::info!("Clipboard monitor started.");

    loop {
      match monitor.recv() {
        Ok(true) => {
          log::info!("Clipboard change detected, sanitizing...");
          let _ = execute_clean(app.clone(), false);
        }
        Ok(false) => {
          log::info!("Clipboard monitor shutting down.");
          break;
        }
        Err(e) => {
          log::error!("Clipboard monitor error: {}", e);
          break;
        }
      }
    }
  });

  rx.recv()
    .expect("Failed to receive clipboard monitor shutdown handle")
}

#[cfg(not(target_os = "windows"))]
fn start_clipboard_monitor(_app: AppHandle) -> ! {
  panic!("Clipboard monitor is only supported on Windows");
}

use crate::clipboard::{ClipboardProvider, SystemClipboard};

pub struct HotkeyState {
  pub active_hotkey: Mutex<String>,
  pub is_conflict: Mutex<bool>,
}

#[tauri::command]
fn get_hotkey_status(state: State<'_, HotkeyState>) -> Result<(String, bool), String> {
  let hotkey = state
    .active_hotkey
    .lock()
    .map_err(|e| e.to_string())?
    .clone();

  let is_conflict = *state.is_conflict.lock().map_err(|e| e.to_string())?;

  Ok((hotkey, is_conflict))
}

pub fn register_hotkey(app: &AppHandle, hotkey_str: &str) -> Result<(), String> {
  if is_hotkey_ctrl_c(hotkey_str) {
    log::info!("Hotkey is Ctrl+C — clipboard monitor handles this case.");
    return Ok(());
  }

  log::info!("Registering global hotkey: {}", hotkey_str);
  let shortcut = Shortcut::from_str(hotkey_str).map_err(|e| e.to_string())?;

  let _ = app.global_shortcut().unregister_all();

  if let Err(e) = app.global_shortcut().register(shortcut) {
    let err_msg = format!("Hotkey conflict: {}", e);

    log::error!("{}", err_msg);

    let _ = app
      .notification()
      .builder()
      .title("CleanCopy - Shortcut Error")
      .body(format!(
        "Failed to register hotkey '{}'. It might be in use.",
        hotkey_str
      ))
      .show();

    return Err(err_msg);
  }

  log::info!("Hotkey '{}' registered successfully.", hotkey_str);
  Ok(())
}

#[tauri::command]
fn execute_clean(app: AppHandle, simulate_copy_first: bool) -> Result<String, String> {
  log::info!(
    "execute_clean invoked (simulate_copy_first={})",
    simulate_copy_first
  );

  if !check_active_license(&app) {
    log::warn!("execute_clean blocked: application is unregistered");
    return Err("CleanCopy is unregistered. Please activate in Settings.".to_string());
  }

  if simulate_copy_first {
    log::info!("Simulating copy event (Ctrl+C)...");
    simulate_copy();
  }

  std::thread::sleep(std::time::Duration::from_millis(100));

  let clipboard = SystemClipboard::new(app.clone());

  let original_text = match clipboard.read_text() {
    Ok(text) => text,
    Err(e) => {
      log::error!("Failed to read clipboard: {}", e);
      return Err(format!("Failed to read clipboard: {}", e));
    }
  };

  if original_text.is_empty() {
    log::info!("Clipboard is empty. Skipping sanitization.");
    return Ok("Clipboard is empty".to_string());
  }

  let config = load_config(&app);
  let sanitized_text = sanitize_text(&original_text, &config);

  if sanitized_text != original_text {
    clipboard.write_text(&sanitized_text).map_err(|e| {
      log::error!("Failed to write cleaned text to clipboard: {}", e);
      e
    })?;

    log::info!(
      "Clipboard sanitized successfully. Original length: {}, Cleaned length: {}",
      original_text.len(),
      sanitized_text.len()
    );

    if config.play_sound {
      #[cfg(target_os = "windows")]
      unsafe {
        MessageBeep(0);
      }

      let _ = app
        .notification()
        .builder()
        .title("CleanCopy")
        .body("Clipboard sanitized!")
        .show();
    }

    Ok("Clipboard sanitized".to_string())
  } else {
    log::info!("Clipboard text did not match any cleaning rules. Skipping.");
    Ok("No cleaning required".to_string())
  }
}

use serde::Serialize;

#[derive(Serialize)]
struct InitialState {
  config: CleanConfig,
  hotkey_conflict: bool,
  hwid: String,
  is_licensed: bool,
}

#[tauri::command]
fn get_initial_state(
  app: AppHandle,
  state: State<'_, HotkeyState>,
) -> Result<InitialState, String> {
  log::info!("get_initial_state invoked");
  let config = load_config(&app);
  let is_conflict = *state.is_conflict.lock().map_err(|e| e.to_string())?;
  let hwid = get_hwid(&app);
  let is_licensed = check_active_license(&app);

  Ok(InitialState {
    config,
    hotkey_conflict: is_conflict,
    hwid,
    is_licensed,
  })
}

#[tauri::command]
fn show_settings_window(window: tauri::WebviewWindow) {
  log::info!("show_settings_window invoked from frontend");
  let _ = window.show();
  let _ = window.set_focus();
}

pub fn open_settings_window(app: &AppHandle) -> Result<(), String> {
  if let Some(window) = app.get_webview_window("settings") {
    let _ = window.show();
    let _ = window.set_focus();
  } else {
    let window = WebviewWindowBuilder::new(app, "settings", WebviewUrl::App("index.html".into()))
      .title("CleanCopy Settings")
      .inner_size(450.0, 600.0)
      .resizable(false)
      .visible(false)
      .build()
      .map_err(|e| e.to_string())?;

    let _ = window.show();
    let _ = window.set_focus();
  }

  Ok(())
}

#[tauri::command]
fn update_config(
  app: AppHandle,
  state: State<'_, HotkeyState>,
  monitor_state: State<'_, ClipboardMonitorState>,
  config: CleanConfig,
) -> Result<(), String> {
  #[cfg(desktop)]
  {
    use tauri_plugin_autostart::ManagerExt;

    let autostart_manager = app.autolaunch();

    if config.autostart {
      let _ = autostart_manager.enable();
    } else {
      let _ = autostart_manager.disable();
    }
  }

  let was_ctrl_c = {
    let h = state.active_hotkey.lock().map_err(|e| e.to_string())?;
    is_hotkey_ctrl_c(&h)
  };
  let now_ctrl_c = is_hotkey_ctrl_c(&config.hotkey);

  let mut conflict = false;

  if was_ctrl_c && !now_ctrl_c {
    if let Ok(mut s) = monitor_state.shutdown.lock() {
      if let Some(shutdown) = s.take() {
        drop(shutdown);
      }
    }
    if register_hotkey(&app, &config.hotkey).is_err() {
      conflict = true;
    }
  } else if !was_ctrl_c && now_ctrl_c {
    let _ = app.global_shortcut().unregister_all();
    let shutdown = start_clipboard_monitor(app.clone());
    if let Ok(mut s) = monitor_state.shutdown.lock() {
      *s = Some(shutdown);
    }
  } else if !was_ctrl_c && !now_ctrl_c {
    if register_hotkey(&app, &config.hotkey).is_err() {
      conflict = true;
    }
  }

  if let Ok(mut h) = state.active_hotkey.lock() {
    *h = config.hotkey.clone();
  }

  if let Ok(mut c) = state.is_conflict.lock() {
    *c = conflict;
  }

  save_config(&app, &config)?;

  Ok(())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
  tauri::Builder::default()
    .plugin(tauri_plugin_opener::init())
    .plugin(tauri_plugin_notification::init())
    .plugin(tauri_plugin_clipboard_manager::init())
    .plugin(tauri_plugin_process::init())
    .plugin(tauri_plugin_autostart::init(
      tauri_plugin_autostart::MacosLauncher::LaunchAgent,
      Some(vec!["--minimized"]),
    ))
    .plugin(
      tauri_plugin_log::Builder::new()
        .targets([
          tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
          tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir {
            file_name: Some("app".to_string()),
          }),
        ])
        .level(log::LevelFilter::Info)
        .build(),
    )
    .plugin(
      tauri_plugin_global_shortcut::Builder::new()
        .with_handler(|app, _shortcut, event| {
          if event.state() == ShortcutState::Pressed {
            log::info!("Global shortcut press event captured.");

            let app_handle = app.clone();

            tauri::async_runtime::spawn(async move {
              if check_active_license(&app_handle) {
                log::info!("License active. Triggering clipboard cleaning.");
                let _ = execute_clean(app_handle, true);
              } else {
                log::warn!("License inactive. Intercepting shortcut and showing settings window.");
                let _ = open_settings_window(&app_handle);
              }
            });
          }
        })
        .build(),
    )
    .manage(HotkeyState {
      active_hotkey: Mutex::new("Alt+Shift+C".to_string()),
      is_conflict: Mutex::new(false),
    })
    .manage(ClipboardMonitorState {
      shutdown: std::sync::Mutex::new(None),
    })
    .invoke_handler(tauri::generate_handler![
      execute_clean,
      get_config,
      update_config,
      get_hotkey_status,
      get_hwid_command,
      activate_license_command,
      is_license_active_command,
      verify_and_save_offline_token,
      get_initial_state,
      show_settings_window,
    ])
    .setup(|app| {
      let app_handle = app.handle().clone();
      let config = load_config(&app_handle);
      let state = app.state::<HotkeyState>();
      let monitor_state = app.state::<ClipboardMonitorState>();

      let mut conflict = false;

      if is_hotkey_ctrl_c(&config.hotkey) {
        let shutdown = start_clipboard_monitor(app_handle.clone());
        if let Ok(mut s) = monitor_state.shutdown.lock() {
          *s = Some(shutdown);
        }
      } else if register_hotkey(&app_handle, &config.hotkey).is_err() {
        conflict = true;
      }

      if let Ok(mut h) = state.active_hotkey.lock() {
        *h = config.hotkey.clone();
      }

      if let Ok(mut c) = state.is_conflict.lock() {
        *c = conflict;
      }

      let clean_i = MenuItem::with_id(
        &app_handle,
        "clean",
        "Clean Clipboard Now",
        true,
        None::<&str>,
      )?;

      let settings_i =
        MenuItem::with_id(&app_handle, "settings", "Settings...", true, None::<&str>)?;

      let quit_i = MenuItem::with_id(&app_handle, "quit", "Quit CleanCopy", true, None::<&str>)?;

      let menu = Menu::with_items(
        &app_handle,
        &[
          &clean_i,
          &settings_i,
          &PredefinedMenuItem::separator(&app_handle)?,
          &quit_i,
        ],
      )?;

      let icon_bytes = include_bytes!("../icons/32x32.png");
      let icon = tauri::image::Image::from_bytes(icon_bytes)
        .map_err(|e| format!("Failed to parse icon bytes: {}", e))?;

      let _tray = TrayIconBuilder::with_id("tray")
        .icon(icon)
        .menu(&menu)
        .on_menu_event(move |app_h, event| match event.id.as_ref() {
          "quit" => {
            SHOULD_EXIT.store(true, Ordering::SeqCst);
            app_h.exit(0);
          }
          "settings" => {
            let _ = open_settings_window(app_h);
          }
          "clean" => {
            let _ = execute_clean(app_h.clone(), false);
          }
          _ => {}
        })
        .build(app)?;

      #[cfg(not(debug_assertions))]
      {
        if !check_active_license(&app_handle) {
          let _ = open_settings_window(&app_handle);
        }
      }

      Ok(())
    })
    .build(tauri::generate_context!())
    .expect("error while building tauri application")
    .run(|_app_handle, event| {
      if let tauri::RunEvent::ExitRequested { api, .. } = event {
        if !SHOULD_EXIT.load(Ordering::SeqCst) {
          api.prevent_exit();
        }
      }
    });
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_is_hotkey_ctrl_c_returns_true_for_ctrl_c() {
    assert!(is_hotkey_ctrl_c("Ctrl+C"));
  }

  #[test]
  fn test_is_hotkey_ctrl_c_returns_false_for_alt_shift_c() {
    assert!(!is_hotkey_ctrl_c("Alt+Shift+C"));
  }

  #[test]
  fn test_is_hotkey_ctrl_c_returns_false_for_ctrl_shift_c() {
    assert!(!is_hotkey_ctrl_c("Ctrl+Shift+C"));
  }

  #[test]
  fn test_is_hotkey_ctrl_c_returns_false_for_ctrl_alt_c() {
    assert!(!is_hotkey_ctrl_c("Ctrl+Alt+C"));
  }

  #[test]
  fn test_is_hotkey_ctrl_c_returns_false_for_ctrl_v() {
    assert!(!is_hotkey_ctrl_c("Ctrl+V"));
  }

  #[test]
  fn test_is_hotkey_ctrl_c_returns_false_for_invalid_string() {
    assert!(!is_hotkey_ctrl_c("not-a-hotkey"));
  }
}
