use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use tauri::{AppHandle, Manager};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum PaneRole {
    MainAgent,
    Dev,
    Shell,
    Git,
    Logs,
    Bridge,
    Custom,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaneDefinition {
    pub id: String,
    pub role: PaneRole,
    pub title: String,
    pub cwd: String,
    pub startup_command: Option<String>,
    pub is_main: bool,
    pub size_weight: f32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LayoutDefinition {
    pub direction: String,
    pub panes: Vec<PaneDefinition>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceDefinition {
    pub id: String,
    pub name: String,
    pub root_path: String,
    #[serde(default = "default_preset")]
    pub preset: String,
    pub layout: LayoutDefinition,
    pub vigthoria: VigthoriaSettings,
    pub updated_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VigthoriaSettings {
    pub debug_mode: bool,
    pub auto_approve: bool,
    pub auto_save: bool,
    pub cli_path: Option<String>,
}

pub struct WorkspaceStore {
    inner: parking_lot::Mutex<WorkspaceStoreInner>,
}

struct WorkspaceStoreInner {
    items: Vec<WorkspaceDefinition>,
    loaded: bool,
}

impl Default for WorkspaceStore {
    fn default() -> Self {
        Self {
            inner: parking_lot::Mutex::new(WorkspaceStoreInner {
                items: Vec::new(),
                loaded: false,
            }),
        }
    }
}

impl WorkspaceStore {
    pub fn load_from_disk(&self, app: &AppHandle) {
        let mut store = self.inner.lock();
        if store.loaded {
            return;
        }
        let path = store_path(app);
        if let Ok(raw) = fs::read_to_string(&path) {
            if let Ok(parsed) = serde_json::from_str::<Vec<WorkspaceDefinition>>(&raw) {
                if !parsed.is_empty() {
                    store.items = parsed.into_iter().map(normalize_workspace_paths).collect();
                }
            }
        }
        store.loaded = true;
    }

    pub fn list(&self) -> Vec<WorkspaceDefinition> {
        self.inner.lock().items.clone()
    }

    pub fn save(&self, workspace: WorkspaceDefinition) -> WorkspaceDefinition {
        let mut store = self.inner.lock();
        let mut next = normalize_workspace_paths(workspace);
        if next.id.trim().is_empty() {
            next.id = Uuid::new_v4().to_string();
        }
        next.updated_at = chrono_now();

        if let Some(idx) = store.items.iter().position(|w| w.id == next.id) {
            store.items[idx] = next.clone();
        } else {
            store.items.push(next.clone());
        }
        next
    }

    pub fn delete(&self, id: &str) -> Result<(), String> {
        let mut store = self.inner.lock();
        let before = store.items.len();
        store.items.retain(|w| w.id != id);
        if store.items.len() == before {
            return Err(format!("Workspace not found: {id}"));
        }
        Ok(())
    }

    pub fn persist(&self, app: &AppHandle) -> Result<(), String> {
        let store = self.inner.lock();
        let path = store_path(app);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).map_err(|e| e.to_string())?;
        }
        let json = serde_json::to_string_pretty(&store.items).map_err(|e| e.to_string())?;
        fs::write(path, json).map_err(|e| e.to_string())?;
        Ok(())
    }
}

fn store_path(app: &AppHandle) -> PathBuf {
    app.path()
        .app_data_dir()
        .unwrap_or_else(|_| PathBuf::from("."))
        .join("workspaces.json")
}

fn default_preset() -> String {
    "agentDev".into()
}

fn normalize_workspace_paths(mut workspace: WorkspaceDefinition) -> WorkspaceDefinition {
    let root = workspace.root_path.trim();
    if !root.is_empty() && root != "." {
        for pane in &mut workspace.layout.panes {
            let cwd = pane.cwd.trim();
            if cwd.is_empty() || cwd == "." {
                pane.cwd = root.to_string();
            }
        }
    }
    workspace
}

fn chrono_now() -> String {
    use std::time::{SystemTime, UNIX_EPOCH};
    let ms = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis())
        .unwrap_or(0);
    ms.to_string()
}
