use serde::{Deserialize, Serialize};
use std::collections::HashMap;
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()
}

pub fn default_workspaces() -> Vec<WorkspaceDefinition> {
    vec![
        workspace_preset_agent_dev("Sample Project", "."),
        workspace_preset_agent_solo("Agent Solo", "."),
    ]
}

pub fn workspace_preset_agent_solo(name: &str, root: &str) -> WorkspaceDefinition {
    WorkspaceDefinition {
        id: Uuid::new_v4().to_string(),
        name: name.into(),
        root_path: root.into(),
        preset: "agentSolo".into(),
        layout: LayoutDefinition {
            direction: "horizontal".into(),
            panes: vec![pane_main_agent(root, 1.0)],
        },
        vigthoria: default_vigthoria_settings(),
        updated_at: chrono_now(),
    }
}

pub fn workspace_preset_agent_dev(name: &str, root: &str) -> WorkspaceDefinition {
    WorkspaceDefinition {
        id: Uuid::new_v4().to_string(),
        name: name.into(),
        root_path: root.into(),
        preset: "agentDev".into(),
        layout: LayoutDefinition {
            direction: "horizontal".into(),
            panes: vec![
                pane_main_agent(root, 0.62),
                PaneDefinition {
                    id: Uuid::new_v4().to_string(),
                    role: PaneRole::Dev,
                    title: "Dev Server".into(),
                    cwd: root.into(),
                    startup_command: Some("npm run dev".into()),
                    is_main: false,
                    size_weight: 0.38,
                },
            ],
        },
        vigthoria: default_vigthoria_settings(),
        updated_at: chrono_now(),
    }
}

fn pane_main_agent(root: &str, weight: f32) -> PaneDefinition {
    PaneDefinition {
        id: Uuid::new_v4().to_string(),
        role: PaneRole::MainAgent,
        title: "Main Agent".into(),
        cwd: root.into(),
        startup_command: Some("vigthoria agent".into()),
        is_main: true,
        size_weight: weight,
    }
}

fn default_vigthoria_settings() -> VigthoriaSettings {
    VigthoriaSettings {
        debug_mode: false,
        auto_approve: false,
        auto_save: true,
        cli_path: None,
    }
}

pub fn vigthoria_env(
    settings: &VigthoriaSettings,
    workspace_root: &str,
) -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.insert("VIGTHORIA_NO_SESSION_MENU".into(), "1".into());
    env.insert("VIGTHORIA_WORKSPACE".into(), workspace_root.into());
    env.insert(
        "VIGTHORIA_DEBUG".into(),
        if settings.debug_mode { "1" } else { "0" }.into(),
    );
    env.insert(
        "VIGTHORIA_AUTO_APPROVE".into(),
        if settings.auto_approve { "1" } else { "0" }.into(),
    );
    env.insert(
        "VIGTHORIA_AUTO_SAVE".into(),
        if settings.auto_save { "1" } else { "0" }.into(),
    );
    env.insert("VIGTHORIA_DECK_MODE".into(), "1".into());
    env
}
