mod audio;
mod models;
mod state;
mod subtitle;
mod task;
mod translate;
mod vad;
mod whisper;

use models::{DefaultModelPaths, StartTaskPayload, SubtitleSegment, SubtitleTask};
use state::AppState;
use tauri::{
    menu::{MenuBuilder, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder},
    AppHandle, Emitter, Manager, PhysicalSize, Size,
};
#[cfg(target_os = "macos")]
use objc2_app_kit::NSWindow;
#[cfg(target_os = "macos")]
use objc2_foundation::NSSize;

const WINDOW_RATIO_WIDTH: f64 = 16.0;
const WINDOW_RATIO_HEIGHT: f64 = 10.0;
const DEFAULT_WINDOW_WIDTH: u32 = 1440;
const DEFAULT_WINDOW_HEIGHT: u32 = 900;
const MIN_WINDOW_WIDTH: u32 = 1280;
const MIN_WINDOW_HEIGHT: u32 = 800;

#[tauri::command]
async fn start_subtitle_task(
    app: tauri::AppHandle,
    window: tauri::Window,
    state: tauri::State<'_, AppState>,
    payload: StartTaskPayload,
) -> std::result::Result<SubtitleTask, String> {
    task::start_task(app, window, state, payload)
        .await
        .map_err(error_to_string)
}

#[tauri::command]
fn list_tasks(state: tauri::State<'_, AppState>) -> std::result::Result<Vec<SubtitleTask>, String> {
    task::list_tasks(state).map_err(error_to_string)
}

#[tauri::command]
fn update_segment_text(
    state: tauri::State<'_, AppState>,
    segment: SubtitleSegment,
) -> std::result::Result<SubtitleTask, String> {
    task::update_segment_text(state, segment).map_err(error_to_string)
}

#[tauri::command]
fn export_subtitles(
    state: tauri::State<'_, AppState>,
    task_id: String,
    format: String,
) -> std::result::Result<String, String> {
    task::export_task(state, task_id, format).map_err(error_to_string)
}

#[tauri::command]
fn get_default_model_paths(app: tauri::AppHandle) -> std::result::Result<DefaultModelPaths, String> {
    task::get_default_model_paths(&app).map_err(error_to_string)
}

fn error_to_string(error: anyhow::Error) -> String {
    format!("{error:#}")
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .manage(AppState::default())
        .setup(|app| {
            configure_window(app.handle())?;
            #[cfg(target_os = "macos")]
            configure_macos_menu(app.handle())?;
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            start_subtitle_task,
            list_tasks,
            update_segment_text,
            export_subtitles,
            get_default_model_paths
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

fn configure_window(app: &AppHandle) -> tauri::Result<()> {
    if let Some(window) = app.get_webview_window("main") {
        window.set_min_size(Some(Size::Physical(PhysicalSize::new(
            MIN_WINDOW_WIDTH,
            MIN_WINDOW_HEIGHT,
        ))))?;
        window.set_size(Size::Physical(PhysicalSize::new(
            DEFAULT_WINDOW_WIDTH,
            DEFAULT_WINDOW_HEIGHT,
        )))?;
        #[cfg(target_os = "macos")]
        apply_macos_aspect_ratio(&window)?;
    }
    Ok(())
}

#[cfg(target_os = "macos")]
fn configure_macos_menu(app: &AppHandle) -> tauri::Result<()> {
    let app_name = app.package_info().name.clone();

    let app_menu = SubmenuBuilder::new(app, app_name)
        .item(&PredefinedMenuItem::about(app, None, None)?)
        .separator()
        .item(&PredefinedMenuItem::services(app, None)?)
        .separator()
        .item(&PredefinedMenuItem::hide(app, None)?)
        .item(&PredefinedMenuItem::hide_others(app, None)?)
        .item(&PredefinedMenuItem::show_all(app, None)?)
        .separator()
        .item(&PredefinedMenuItem::quit(app, None)?)
        .build()?;

    let file_menu = SubmenuBuilder::new(app, "文件")
        .item(&MenuItemBuilder::with_id("pick_files", "选择媒体文件").accelerator("CmdOrCtrl+O").build(app)?)
        .separator()
        .item(&MenuItemBuilder::with_id("export_srt", "导出 SRT").build(app)?)
        .item(&MenuItemBuilder::with_id("export_vtt", "导出 VTT").build(app)?)
        .item(&MenuItemBuilder::with_id("export_ass", "导出 ASS").build(app)?)
        .build()?;

    let edit_menu = SubmenuBuilder::new(app, "编辑")
        .item(&PredefinedMenuItem::undo(app, None)?)
        .item(&PredefinedMenuItem::redo(app, None)?)
        .separator()
        .item(&PredefinedMenuItem::cut(app, None)?)
        .item(&PredefinedMenuItem::copy(app, None)?)
        .item(&PredefinedMenuItem::paste(app, None)?)
        .item(&PredefinedMenuItem::select_all(app, None)?)
        .build()?;

    let settings_menu = SubmenuBuilder::new(app, "设置")
        .item(&MenuItemBuilder::with_id("toggle_advanced", "显示或隐藏高级设置").build(app)?)
        .item(&MenuItemBuilder::with_id("toggle_bilingual", "切换双语导出").build(app)?)
        .item(&MenuItemBuilder::with_id("reset_models", "恢复默认模型路径").build(app)?)
        .build()?;

    let language_menu = SubmenuBuilder::new(app, "语言")
        .item(&MenuItemBuilder::with_id("set_lang_zh_cn", "中文").build(app)?)
        .item(&MenuItemBuilder::with_id("set_lang_en", "English").build(app)?)
        .build()?;

    let window_menu = SubmenuBuilder::new(app, "窗口")
        .item(&PredefinedMenuItem::minimize(app, None)?)
        .item(&PredefinedMenuItem::maximize(app, None)?)
        .separator()
        .item(&PredefinedMenuItem::close_window(app, None)?)
        .build()?;

    let menu = MenuBuilder::new(app)
        .item(&app_menu)
        .item(&file_menu)
        .item(&edit_menu)
        .item(&settings_menu)
        .item(&language_menu)
        .item(&window_menu)
        .build()?;

    app.set_menu(menu)?;
    app.on_menu_event(|app, event| {
        let action = match event.id().0.as_str() {
            "pick_files" => Some("pick-files"),
            "export_srt" => Some("export-srt"),
            "export_vtt" => Some("export-vtt"),
            "export_ass" => Some("export-ass"),
            "toggle_advanced" => Some("toggle-advanced"),
            "toggle_bilingual" => Some("toggle-bilingual"),
            "reset_models" => Some("reset-models"),
            "set_lang_zh_cn" => Some("set-lang-zh-CN"),
            "set_lang_en" => Some("set-lang-en"),
            _ => None,
        };

        if let Some(action) = action {
            let _ = app.emit("menu:action", action);
        }
    });

    Ok(())
}

#[cfg(not(target_os = "macos"))]
fn configure_macos_menu(_app: &AppHandle) -> tauri::Result<()> {
    Ok(())
}

#[cfg(target_os = "macos")]
fn apply_macos_aspect_ratio(window: &tauri::WebviewWindow) -> tauri::Result<()> {
    let ns_window = window.ns_window()?;
    let ns_window = ns_window.cast::<NSWindow>();
    let aspect_ratio = NSSize::new(WINDOW_RATIO_WIDTH, WINDOW_RATIO_HEIGHT);

    unsafe {
        let ns_window = &*ns_window;
        ns_window.setContentAspectRatio(aspect_ratio);
    }

    Ok(())
}
