use std::fs;
use std::path::PathBuf;
use rand::seq::SliceRandom;
use serde_json::Value;

const RAW_DATA_VERSION: &str = "v0.1";

fn get_samples_dir() -> PathBuf {
    std::env::var("HOME").ok()
        .map(|h| PathBuf::from(h).join(".local/share/aqua/samples"))
        .unwrap_or_else(|| PathBuf::from(".").join("aqua_samples"))
}

fn load_raw(network: &str, datatype: &str) -> Option<Vec<String>> {
    let dir = get_samples_dir();
    for size in ["10k", "1k", "100"] {
        let path = dir.join(format!("{}_{}_{}_{}.json", network, datatype, size, RAW_DATA_VERSION));
        if path.exists() {
            if let Ok(content) = fs::read_to_string(&path) {
                if let Ok(data) = serde_json::from_str::<Vec<Value>>(&content) {
                    return Some(data.into_iter().filter_map(|v| v.as_str().map(String::from)).collect());
                }
            }
        }
    }
    None
}

fn sample<R: rand::Rng>(mut data: Vec<String>, n: usize, rng: &mut R) -> Vec<String> {
    if n > data.len() && !data.is_empty() {
        let orig = data.clone();
        while data.len() < n {
            data.extend(orig.clone());
        }
    }
    data.shuffle(rng);
    data.truncate(n);
    data
}

fn rng(seed: Option<u64>) -> Box<dyn rand::RngCore> {
    use rand::SeedableRng;
    seed.map(|s| Box::new(rand::rngs::StdRng::seed_from_u64(s)) as Box<dyn rand::RngCore>)
        .unwrap_or_else(|| Box::new(rand::thread_rng()))
}

fn load_samples(
    n: usize,
    network: &str,
    datatype: &str,
    fallback: fn() -> Vec<String>,
    seed: Option<u64>,
) -> Vec<String> {
    let mut r = rng(seed);
    if let Some(data) = load_raw(network, datatype) {
        if !data.is_empty() {
            return sample(data, n, &mut r);
        }
    }
    sample(fallback(), n, &mut r)
}

pub fn accounts(n: usize, network: &str, seed: Option<u64>) -> Vec<String> {
    load_samples(n, network, "accounts", fallback_accounts, seed)
}

pub fn token_accounts(n: usize, network: &str, seed: Option<u64>) -> Vec<String> {
    load_samples(n, network, "token_accounts", fallback_token_accounts, seed)
}

pub fn transactions(n: usize, network: &str, seed: Option<u64>) -> Vec<String> {
    load_samples(n, network, "transactions", fallback_transactions, seed)
}

pub fn programs(n: usize, network: &str, seed: Option<u64>) -> Vec<String> {
    load_samples(n, network, "programs", fallback_programs, seed)
}

fn fallback_accounts() -> Vec<String> {
    vec![
        "11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "So11111111111111111111111111111111111111112",
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
        "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj", "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
        "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
    ].into_iter().map(String::from).collect()
}

fn fallback_token_accounts() -> Vec<String> {
    vec![
        "13s3D4P6i562md61GQWXwDM5H4NekQCGjpW5DB66bxdA", "wmAjGwwMPk7CNXiHNzy3Z3bBPdT7QaXf8c5C4eE2FwX",
        "2zojZNiiPTa9owQMSV63hNrvfEC2zWdtbPV1TB4XsMVU", "5j6cq12ReLuGv9yefFi6s7sEveeEwMGccf5rAeEyyUcf",
        "69DaQZ8ZB8swwqJBftc44RRtw46iBz5wegtJfPpDMocK", "6Xniq36HXQjwpi8rYjsWaRH7S1vjm76gU5ewyeHZXAgD",
    ].into_iter().map(String::from).collect()
}

fn fallback_transactions() -> Vec<String> {
    vec![
        "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
        "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv",
        "yPFHoSYz8GbFvKxrz9kHuPmXcYjKGpCt6a2dfxJAJCvxD7tFDqcSz2dfb3MdNxGJ2vWu8TSwQjC9W2KMGK2AZHm",
        "4EtmFKogY3jjJvtSfUD1kZjKqNPLjhV9DAqaRPP9mJpY3hULHpjYxcJkP4bDGCnxBrxvuukt1dAqhjT49aKS3dLR",
    ].into_iter().map(String::from).collect()
}

fn fallback_programs() -> Vec<String> {
    vec![
        "11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
        "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
    ].into_iter().map(String::from).collect()
}
