use std::path::PathBuf;

use crate::error::{Error, Result};
use crate::types::StoredAccessToken;

pub struct TokenStore {
    config_dir: PathBuf,
}

impl TokenStore {
    pub fn new(config_dir: PathBuf) -> Self {
        Self { config_dir }
    }

    pub fn load(&self) -> Option<String> {
        let path = self.token_path()?;
        let contents = std::fs::read_to_string(path).ok()?;
        serde_json::from_str::<StoredAccessToken>(&contents)
            .ok()
            .map(|stored| stored.access_token)
    }

    pub fn delete(&self) -> Result<()> {
        if let Some(path) = self.token_path() {
            if path.exists() {
                std::fs::remove_file(path)?;
            }
        }
        Ok(())
    }

    pub fn save(&self, token: &str) -> Result<()> {
        let path = self
            .token_path()
            .ok_or_else(|| Error::api("No config directory"))?;
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent)?;
        }
        let contents = serde_json::to_string(&StoredAccessToken {
            access_token: token.to_string(),
        })?;
        std::fs::write(path, contents)?;
        Ok(())
    }

    fn token_path(&self) -> Option<PathBuf> {
        let base_dir = if cfg!(windows) {
            std::env::var_os("APPDATA").map(PathBuf::from)
        } else {
            std::env::var_os("XDG_CONFIG_HOME")
                .map(PathBuf::from)
                .or_else(|| {
                    std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".config"))
                })
        }?;
        Some(
            base_dir
                .join(&self.config_dir)
                .join("nodos-store")
                .join("access-token.json"),
        )
    }
}

pub enum Auth {
    Token(String),
    None,
}

impl Auth {
    pub fn from_env() -> Option<Self> {
        std::env::var("NODOS_STORE_ACCESS_TOKEN")
            .ok()
            .map(Auth::Token)
    }

    pub fn token(&self) -> Option<&str> {
        match self {
            Auth::Token(t) => Some(t),
            Auth::None => None,
        }
    }
}
