//! bt-keygen — Private CLI tool for generating and managing licenses.
//!
//! Usage:
//!   bt-keygen generate-keys              Generate a new Ed25519 key pair
//!   bt-keygen issue --email <email>      Issue a new Pro license
//!   bt-keygen verify <license-key>       Verify a license key

use base64::Engine as _;
use chrono::{Duration, Utc};
use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
use serde::{Deserialize, Serialize};
use std::fs;

const B64: base64::engine::general_purpose::GeneralPurpose =
    base64::engine::general_purpose::URL_SAFE_NO_PAD;

/// Same structs as in the lib, duplicated here so the keygen
/// is fully standalone (not linked to the distributed binary).
#[derive(Debug, Clone, Serialize, Deserialize)]
enum LicenseFeature {
    Pro,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct LicensePayload {
    email: String,
    license_id: String,
    features: Vec<LicenseFeature>,
    max_devices: u32,
    updates_until: chrono::DateTime<Utc>,
    issued_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct License {
    payload: LicensePayload,
    signature: String,
}

fn main() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() < 2 {
        eprintln!("Usage:");
        eprintln!("  bt-keygen generate-keys");
        eprintln!("  bt-keygen issue --email <email> [--days <update_days>]");
        eprintln!("  bt-keygen verify <license-key>");
        std::process::exit(1);
    }

    match args[1].as_str() {
        "generate-keys" => cmd_generate_keys(),
        "issue" => cmd_issue(&args[2..]),
        "verify" => cmd_verify(&args[2..]),
        _ => {
            eprintln!("Unknown command: {}", args[1]);
            std::process::exit(1);
        }
    }
}

fn cmd_generate_keys() {
    let signing_key = SigningKey::generate(&mut rand_core_06::OsRng);
    let verifying_key = signing_key.verifying_key();

    // Save private key (KEEP SECRET)
    let sk_bytes = signing_key.to_bytes();
    let sk_b64 = B64.encode(sk_bytes);
    fs::write("private.key", &sk_b64).expect("Failed to write private.key");

    // Save public key
    let vk_bytes = verifying_key.to_bytes();
    let vk_b64 = B64.encode(vk_bytes);
    fs::write("public.key", &vk_b64).expect("Failed to write public.key");

    // Print the public key as Rust array for embedding in license.rs
    print!("// Paste this into license.rs PUBLIC_KEY_BYTES:\nconst PUBLIC_KEY_BYTES: [u8; 32] = [");
    for (i, b) in vk_bytes.iter().enumerate() {
        if i % 8 == 0 {
            print!("\n    ");
        }
        print!("0x{:02x}, ", b);
    }
    println!("\n];");

    println!("\nKeys generated:");
    println!("  private.key (KEEP SECRET — never commit this)");
    println!("  public.key");
}

fn cmd_issue(args: &[String]) {
    let mut email = String::new();
    let mut days: i64 = 365;

    let mut i = 0;
    while i < args.len() {
        match args[i].as_str() {
            "--email" => {
                i += 1;
                email = args[i].to_string();
            }
            "--days" => {
                i += 1;
                days = args[i].parse().expect("Invalid days");
            }
            _ => {
                eprintln!("Unknown flag: {}", args[i]);
                std::process::exit(1);
            }
        }
        i += 1;
    }

    if email.is_empty() {
        eprintln!("--email is required");
        std::process::exit(1);
    }

    // Load private key
    let sk_b64 =
        fs::read_to_string("private.key").expect("private.key not found — run generate-keys first");
    let sk_bytes = B64.decode(sk_b64.trim()).expect("Invalid private key");
    let sk_array: [u8; 32] = sk_bytes.try_into().expect("Invalid key length");
    let signing_key = SigningKey::from_bytes(&sk_array);

    let payload = LicensePayload {
        email: email.clone(),
        license_id: uuid::Uuid::new_v4().to_string(),
        features: vec![LicenseFeature::Pro],
        max_devices: 2,
        updates_until: Utc::now() + Duration::days(days),
        issued_at: Utc::now(),
    };

    let payload_json = serde_json::to_string(&payload).unwrap();
    let signature = signing_key.sign(payload_json.as_bytes());
    let sig_b64 = B64.encode(signature.to_bytes());

    let license = License {
        payload,
        signature: sig_b64,
    };

    let license_json = serde_json::to_string(&license).unwrap();
    let license_key = B64.encode(license_json.as_bytes());

    println!("License issued for: {}", email);
    println!("Updates valid for: {} days", days);
    println!("Max devices: 2");
    println!("\n--- LICENSE KEY ---");
    println!("{}", license_key);
    println!("--- END ---");
}

fn cmd_verify(args: &[String]) {
    if args.is_empty() {
        eprintln!("Usage: bt-keygen verify <license-key>");
        std::process::exit(1);
    }

    let key = &args[0];
    let bytes = B64.decode(key.trim()).expect("Invalid base64");

    let license: License = serde_json::from_slice(&bytes).expect("Invalid license format");

    println!("Email:        {}", license.payload.email);
    println!("License ID:   {}", license.payload.license_id);
    println!("Max devices:  {}", license.payload.max_devices);
    println!("Issued:       {}", license.payload.issued_at);
    println!("Updates until: {}", license.payload.updates_until);
    println!("Features:     {:?}", license.payload.features);

    // Load public key and verify
    let vk_b64 = fs::read_to_string("public.key").expect("public.key not found");
    let vk_bytes = B64.decode(vk_b64.trim()).expect("Invalid public key");
    let vk_array: [u8; 32] = vk_bytes.try_into().expect("Invalid key length");
    let verifying_key = VerifyingKey::from_bytes(&vk_array).expect("Invalid public key");

    let payload_json = serde_json::to_string(&license.payload).unwrap();
    let sig_bytes = B64
        .decode(&license.signature)
        .expect("Invalid signature encoding");
    let signature = ed25519_dalek::Signature::from_slice(&sig_bytes).expect("Invalid signature");

    use ed25519_dalek::Verifier;
    match verifying_key.verify(payload_json.as_bytes(), &signature) {
        Ok(()) => println!("\nSignature VALID"),
        Err(_) => println!("\nSignature INVALID"),
    }
}
