use rcgen::{CertificateParams, DistinguishedName, DnType, IsCa, SanType};
use std::{
    fs,
    io::Write,
    net::{IpAddr, Ipv4Addr},
    path::{Path, PathBuf},
};
use time::{Duration, OffsetDateTime};

fn config_dir() -> PathBuf {
    std::env::var_os("XDG_CONFIG_HOME")
        .map(PathBuf::from)
        .or_else(|| std::env::var_os("HOME").map(|h| Path::new(&h).join(".config")))
        .unwrap_or_else(|| PathBuf::from("."))
        .join("socktop_agent")
        .join("tls")
}

pub fn cert_paths() -> (PathBuf, PathBuf) {
    let dir = config_dir();
    (dir.join("cert.pem"), dir.join("key.pem"))
}

pub fn ensure_self_signed_cert() -> anyhow::Result<(PathBuf, PathBuf)> {
    let (cert_path, key_path) = cert_paths();
    if cert_path.exists() && key_path.exists() {
        return Ok((cert_path, key_path));
    }
    fs::create_dir_all(cert_path.parent().unwrap())?;

    let hostname = hostname::get()
        .ok()
        .and_then(|s| s.into_string().ok())
        .unwrap_or_else(|| "localhost".to_string());

    let mut params = CertificateParams::new(vec![hostname.clone(), "localhost".into()])?;
    params
        .subject_alt_names
        .push(SanType::IpAddress(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
    params.subject_alt_names.push(SanType::IpAddress(IpAddr::V6(
        ::std::net::Ipv6Addr::LOCALHOST,
    )));
    params
        .subject_alt_names
        .push(SanType::IpAddress(IpAddr::V4(Ipv4Addr::UNSPECIFIED)));

    // Allow operator to provide extra SANs (comma-separated), e.g. IPs or DNS names
    if let Ok(extra) = std::env::var("SOCKTOP_AGENT_EXTRA_SANS") {
        for raw in extra.split(',') {
            let s = raw.trim();
            if s.is_empty() {
                continue;
            }
            if let Ok(ip) = s.parse::<IpAddr>() {
                params.subject_alt_names.push(SanType::IpAddress(ip));
            } else {
                match s.to_string().try_into() {
                    Ok(dns) => params.subject_alt_names.push(SanType::DnsName(dns)),
                    Err(_) => eprintln!("socktop_agent: ignoring invalid SAN entry: {s}"),
                }
            }
        }
    }

    let mut dn = DistinguishedName::new();
    dn.push(DnType::CommonName, hostname.clone());
    params.distinguished_name = dn;
    params.is_ca = IsCa::NoCa;
    // Dynamic validity: start slightly in the past to avoid clock skew issues, end ~397 days later
    let now = OffsetDateTime::now_utc();
    params.not_before = now - Duration::minutes(5);
    params.not_after = now + Duration::days(397);

    // Generate key pair (default is ECDSA P256 SHA256)
    let key_pair = rcgen::KeyPair::generate()?; // defaults to ECDSA P256 SHA256
    let cert = params.self_signed(&key_pair)?;
    let cert_pem = cert.pem();
    let key_pem = key_pair.serialize_pem();

    let mut f = fs::File::create(&cert_path)?;
    f.write_all(cert_pem.as_bytes())?;
    let mut k = fs::File::create(&key_path)?;
    k.write_all(key_pem.as_bytes())?;

    println!(
        "socktop_agent: generated self-signed TLS certificate at {}",
        cert_path.display()
    );
    println!("socktop_agent: private key at {}", key_path.display());
    Ok((cert_path, key_path))
}
