//! Tauri-managed application state.
//!
//! The non-Tauri composition root lives in `backend::BackendCore`. This type is
//! the desktop driving-adapter envelope: it adds Tauri transport bridges and
//! OS-window focus state, then dereferences to the shared core so existing
//! commands keep their field/method access unchanged.

use std::collections::HashMap;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use backend::BackendCore;
use domain::PersistedPluginLayoutWindow;
use infrastructure::{TemplateToolProvider, TicketToolProvider};
use serde::{Deserialize, Serialize};

use crate::chat::ChatBridge;
use crate::embedded_server::EmbeddedServerController;
use crate::pty::PtyBridge;
use crate::templates::AppTemplateToolProvider;
use crate::tickets::AppTicketToolProvider;

pub use backend::ResumeContext;

/// Focused project snapshot shared with detached panel windows.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FocusedProjectDto {
    /// Project id as a string.
    pub id: String,
    /// Project display name.
    pub name: String,
    /// Absolute project root.
    pub root: String,
}

/// Desktop adapter state managed by Tauri.
pub struct AppState {
    core: Arc<BackendCore>,
    /// Generic PTY to Tauri Channel bridge registry.
    pub pty_bridge: Arc<PtyBridge>,
    /// Structured reply to Tauri Channel bridge registry.
    pub chat_bridge: Arc<ChatBridge>,
    /// Desktop-owned embedded HTTP server lifecycle manager.
    pub embedded_server: Arc<EmbeddedServerController>,
    /// Project currently focused by the main window; panel-only windows follow it.
    focused_project: Mutex<Option<FocusedProjectDto>>,
    /// Detached plugin-layout surfaces keyed by Tauri window label.
    plugin_window_surfaces: Mutex<HashMap<String, PersistedPluginLayoutWindow>>,
}

impl AppState {
    /// Builds the shared backend core and wraps it with desktop-only transport
    /// state. `app_data_dir` is resolved by the Tauri adapter before crossing
    /// into the backend core.
    #[must_use]
    pub fn build(app_data_dir: PathBuf) -> Self {
        Self::build_with_resource_dir(app_data_dir, None)
    }

    /// Builds the shared backend core with the Tauri bundle resource directory
    /// available to desktop-only adapters.
    #[must_use]
    pub fn build_with_resource_dir(app_data_dir: PathBuf, resource_dir: Option<PathBuf>) -> Self {
        let core = Arc::new(BackendCore::build(app_data_dir.clone()));
        core.ticket_tool_binder
            .bind(Arc::new(AppTicketToolProvider {
                create: Arc::clone(&core.create_issue),
                read: Arc::clone(&core.read_issue),
                list: Arc::clone(&core.list_issues),
                update: Arc::clone(&core.update_issue),
                bulk_update_status: Arc::clone(&core.bulk_update_issue_status),
                bulk_update_priority: Arc::clone(&core.bulk_update_issue_priority),
                bulk_delete: Arc::clone(&core.bulk_delete_issues),
                read_carnet: Arc::clone(&core.read_issue_carnet),
                update_carnet: Arc::clone(&core.update_issue_carnet),
                add_attachment: Arc::clone(&core.add_issue_attachment),
                read_attachment: Arc::clone(&core.read_issue_attachment),
                mark_attachment_summarized: Arc::clone(&core.mark_issue_attachment_summarized),
                link: Arc::clone(&core.link_issues),
                unlink: Arc::clone(&core.unlink_issues),
                list_sprints: Arc::clone(&core.list_sprints),
            }) as Arc<dyn TicketToolProvider>);
        core.template_tool_binder
            .bind(Arc::new(AppTemplateToolProvider {
                create: Arc::clone(&core.create_template),
                read: Arc::clone(&core.read_template),
                list: Arc::clone(&core.list_templates),
                update: Arc::clone(&core.update_template),
                delete: Arc::clone(&core.delete_template),
            }) as Arc<dyn TemplateToolProvider>);

        Self {
            core,
            pty_bridge: Arc::new(PtyBridge::new()),
            chat_bridge: Arc::new(ChatBridge::new()),
            embedded_server: Arc::new(EmbeddedServerController::with_resource_dir(
                app_data_dir,
                resource_dir,
            )),
            focused_project: Mutex::new(None),
            plugin_window_surfaces: Mutex::new(HashMap::new()),
        }
    }

    /// Clones the shared backend core for adapter components that must share
    /// the desktop composition root.
    #[must_use]
    pub fn core(&self) -> Arc<BackendCore> {
        Arc::clone(&self.core)
    }

    /// Updates the focused project state. `None` means no project is focused/open.
    pub fn set_focused_project(&self, project: Option<FocusedProjectDto>) {
        *self
            .focused_project
            .lock()
            .expect("focused project mutex poisoned") = project;
    }

    /// Returns the current focused project, if any.
    #[must_use]
    pub fn get_focused_project(&self) -> Option<FocusedProjectDto> {
        self.focused_project
            .lock()
            .expect("focused project mutex poisoned")
            .clone()
    }

    /// Records the plugin layout surface hosted by a detached window.
    pub fn set_plugin_window_surface(&self, label: String, surface: PersistedPluginLayoutWindow) {
        self.plugin_window_surfaces
            .lock()
            .expect("plugin window surface mutex poisoned")
            .insert(label, surface);
    }

    /// Reads the plugin layout surface hosted by a detached window.
    #[must_use]
    pub fn get_plugin_window_surface(&self, label: &str) -> Option<PersistedPluginLayoutWindow> {
        self.plugin_window_surfaces
            .lock()
            .expect("plugin window surface mutex poisoned")
            .get(label)
            .cloned()
    }

    /// Removes a detached plugin layout surface.
    pub fn clear_plugin_window_surface(&self, label: &str) {
        self.plugin_window_surfaces
            .lock()
            .expect("plugin window surface mutex poisoned")
            .remove(label);
    }
}

impl Deref for AppState {
    type Target = BackendCore;

    fn deref(&self) -> &Self::Target {
        &self.core
    }
}
