use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Project {
    pub name: String,
    pub mpns: Vec<String>,
    #[serde(default)]
    pub description: String,
    #[serde(default)]
    pub created: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProjectStore {
    pub projects: BTreeMap<String, Project>,
    #[serde(default)]
    pub active: Option<String>,
}

fn now_unix() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}

fn store_path() -> PathBuf {
    let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".into());
    let dir = PathBuf::from(home).join(".config/adom-chip-fetcher");
    let _ = std::fs::create_dir_all(&dir);
    dir.join("projects.json")
}

pub fn load() -> ProjectStore {
    let path = store_path();
    match std::fs::read_to_string(&path) {
        Ok(s) => serde_json::from_str(&s).unwrap_or_default(),
        Err(_) => ProjectStore::default(),
    }
}

pub fn save(store: &ProjectStore) -> Result<()> {
    let path = store_path();
    let json = serde_json::to_string_pretty(store)?;
    std::fs::write(&path, json)?;
    Ok(())
}

pub fn add_mpn(project: &str, mpn: &str) -> Result<()> {
    let mut store = load();
    let proj = store.projects.entry(project.to_string()).or_insert_with(|| Project {
        name: project.to_string(),
        mpns: vec![],
        description: String::new(),
        created: now_unix().to_string(),
    });
    if !proj.mpns.contains(&mpn.to_string()) {
        proj.mpns.push(mpn.to_string());
    }
    save(&store)
}

pub fn remove_mpn(project: &str, mpn: &str) -> Result<()> {
    let mut store = load();
    if let Some(proj) = store.projects.get_mut(project) {
        proj.mpns.retain(|m| m != mpn);
    }
    save(&store)
}

pub fn set_active(project: Option<&str>) -> Result<()> {
    let mut store = load();
    store.active = project.map(|s| s.to_string());
    save(&store)
}

pub fn get_active() -> Option<String> {
    load().active
}

pub fn list_projects() -> Vec<Project> {
    load().projects.values().cloned().collect()
}

pub fn get_project(name: &str) -> Option<Project> {
    load().projects.get(name).cloned()
}

pub fn mpns_for_project(name: &str) -> Vec<String> {
    load().projects.get(name).map(|p| p.mpns.clone()).unwrap_or_default()
}

pub fn create_project(name: &str, description: &str) -> Result<()> {
    let mut store = load();
    store.projects.entry(name.to_string()).or_insert_with(|| Project {
        name: name.to_string(),
        mpns: vec![],
        description: description.to_string(),
        created: now_unix().to_string(),
    });
    save(&store)
}

pub fn delete_project(name: &str) -> Result<()> {
    let mut store = load();
    store.projects.remove(name);
    if store.active.as_deref() == Some(name) {
        store.active = None;
    }
    save(&store)
}