//! `ulti-core` — shared native plugin for every app in the Ulti suite.
//!
//! Exposes Tauri commands (invoked from the static web frontend) for:
//! - secure session/config storage (cross-app SSO),
//! - push device-token registration,
//! - device contacts import,
//! - inbound share payload retrieval,
//! - inter-app / external URL opening.
//!
//! Desktop builds (used by `cargo check` in CI without an Android SDK / Xcode)
//! implement everything that can run on a host; mobile-only capabilities that
//! require native (Kotlin/Swift) code return a clear, structured error and are
//! wired in the per-platform plugin layer documented in `mobile/README.md`.

use std::sync::Mutex;

use serde::{Deserialize, Serialize};
use tauri::{
    plugin::{Builder, TauriPlugin},
    Manager, Runtime,
};

mod secure_store;

pub use secure_store::{KEY_CONFIG, KEY_SESSION, SHARED_SERVICE};

/// In-memory queue of share payloads handed to us by the OS (share target).
#[derive(Default)]
struct ShareQueue(Mutex<Vec<SharePayload>>);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SharePayload {
    /// One of: "text", "url", "file", "image".
    pub kind: String,
    pub text: Option<String>,
    pub url: Option<String>,
    /// Absolute paths of shared files (already copied into app storage).
    pub files: Vec<String>,
    pub mime: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct PushRegistration {
    pub platform: String,
    /// FCM (Android) / APNs (iOS) device token. None until the native layer
    /// reports it (or on desktop).
    pub token: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct DeviceContact {
    pub display_name: Option<String>,
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub emails: Vec<String>,
    pub phones: Vec<String>,
    pub organization: Option<String>,
}

fn platform() -> String {
    std::env::consts::OS.to_string()
}

// ---------------------------------------------------------------------------
// Secure store commands
// ---------------------------------------------------------------------------

#[tauri::command]
fn store_set(key: String, value: String) -> Result<(), String> {
    secure_store::set(&key, &value)
}

#[tauri::command]
fn store_get(key: String) -> Result<Option<String>, String> {
    secure_store::get(&key)
}

#[tauri::command]
fn store_delete(key: String) -> Result<(), String> {
    secure_store::delete(&key)
}

#[tauri::command]
fn store_clear() -> Result<(), String> {
    secure_store::clear()
}

// ---------------------------------------------------------------------------
// Push registration
// ---------------------------------------------------------------------------

#[tauri::command]
fn push_register() -> Result<PushRegistration, String> {
    // The actual FCM/APNs token is retrieved by the native push plugin and
    // pushed to the webview via the `ulti://push-token` event. This command
    // returns the platform so the frontend can decide what to register; the
    // token may already be cached in the secure store under "push_token".
    let token = secure_store::get("push_token").unwrap_or(None);
    Ok(PushRegistration {
        platform: platform(),
        token,
    })
}

// ---------------------------------------------------------------------------
// Contacts import
// ---------------------------------------------------------------------------

#[tauri::command]
fn contacts_fetch() -> Result<Vec<DeviceContact>, String> {
    #[cfg(mobile)]
    {
        // Implemented by the native contacts plugin (Android ContactsContract /
        // iOS Contacts framework). See mobile/README.md "Contacts plugin".
        Err("contacts_native_not_wired".to_string())
    }
    #[cfg(not(mobile))]
    {
        Err("contacts_unsupported_on_desktop".to_string())
    }
}

// ---------------------------------------------------------------------------
// Inbound share payloads
// ---------------------------------------------------------------------------

#[tauri::command]
fn share_take_pending<R: Runtime>(
    app: tauri::AppHandle<R>,
) -> Result<Option<SharePayload>, String> {
    let queue = app.state::<ShareQueue>();
    let mut items = queue.0.lock().map_err(|e| e.to_string())?;
    Ok(if items.is_empty() {
        None
    } else {
        Some(items.remove(0))
    })
}

#[derive(Debug, Clone, Deserialize)]
pub struct ShareOut {
    pub text: Option<String>,
    pub url: Option<String>,
    /// Absolute file paths to share out.
    #[serde(default)]
    pub files: Vec<String>,
    pub mime: Option<String>,
}

/// Share content *out* to other apps via the OS share sheet.
#[tauri::command]
fn share_out(payload: ShareOut) -> Result<(), String> {
    #[cfg(mobile)]
    {
        // The native share sheet (Android ACTION_SEND / iOS UIActivityViewController)
        // is invoked by the platform plugin layer. See mobile/README.md "Share".
        let _ = payload;
        Err("share_out_native_not_wired".to_string())
    }
    #[cfg(not(mobile))]
    {
        // Desktop dev fallback: open a URL if present, otherwise no-op.
        if let Some(url) = payload.url.as_deref() {
            open::that(url).map_err(|e| e.to_string())?;
        }
        Ok(())
    }
}

/// Push a share payload onto the queue (called by native share-target glue).
pub fn enqueue_share<R: Runtime>(app: &tauri::AppHandle<R>, payload: SharePayload) {
    if let Some(queue) = app.try_state::<ShareQueue>() {
        if let Ok(mut items) = queue.0.lock() {
            items.push(payload);
        }
    }
}

// ---------------------------------------------------------------------------
// Inter-app / external URL opening
// ---------------------------------------------------------------------------

#[tauri::command]
fn app_open_url(url: String) -> Result<(), String> {
    #[cfg(desktop)]
    {
        open::that(&url).map_err(|e| e.to_string())
    }
    #[cfg(mobile)]
    {
        // On mobile the deep-link / opener plugin handles cross-app launches;
        // this command is a no-op fallback so the frontend stays uniform.
        log::info!("app_open_url (mobile no-op): {url}");
        Ok(())
    }
}

/// Build the plugin. Apps call `.plugin(ulti_core::init())`.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("ulti-core")
        .invoke_handler(tauri::generate_handler![
            store_set,
            store_get,
            store_delete,
            store_clear,
            push_register,
            contacts_fetch,
            share_take_pending,
            share_out,
            app_open_url
        ])
        .setup(|app, _api| {
            app.manage(ShareQueue::default());
            Ok(())
        })
        .build()
}
