use std::fs;
use std::path::{Path, PathBuf};
use std::time::Duration;
use tokio::fs::{create_dir_all, File};
use tokio::io::AsyncWriteExt;
use reqwest::Client;
use futures::stream::{self, StreamExt};

#[tokio::main]
async fn main() {
    // Base directory where the downloads will be saved
    let base_dir = Path::new(r"C:\Users\Lukas\Downloads\lkjhgfd2");

    // List of placeIDs
    let place_ids = vec![
        "10789565990",
        "11181971058",
        "11181982436",
        "11182034319",
        "11182037286",
        "11263735303",
        "11263873956",
        "12647442701",
        "33564694",
        "4568576845",
        "4737314314",
        "4748963513",
        "4956810680",
        "4998492434",
        "5000291183",
        "5063342777",
        "5229799451",
        "5400427674",
        "5465723678",
        "5914914682",
        "5972110179",
        "6087359275",
        "6701464352",
        "6767882272",
        "6768240609",
        "6783491168",
        "7162161171",
        "7730658758",
        "9048939285",
    ];

    // Initialize HTTP client with timeout and other configurations
    let client = Client::builder()
        .timeout(Duration::from_secs(30))
        .build()
        .expect("Failed to build HTTP client");

    // Process each placeID concurrently with a limit on concurrency
    let concurrency_limit = 5; // Adjust based on your network capability
    stream::iter(place_ids)
        .for_each_concurrent(concurrency_limit, |place_id| {
            let client = client.clone();
            let base_dir = base_dir.to_path_buf();
            async move {
                if let Err(e) = process_place_id(&client, &base_dir, place_id).await {
                    eprintln!("Error processing placeID {}: {}", place_id, e);
                }
            }
        })
        .await;

    println!("All downloads completed.");
}

/// Processes a single placeID:
/// - Reads index.txt
/// - Parses entries
/// - Downloads assets
async fn process_place_id(client: &Client, base_dir: &PathBuf, place_id: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("Processing placeID: {}", place_id);

    // Path to index.txt
    let index_path = base_dir.join(place_id).join("index.txt");

    // Read index.txt content
    let index_content = match fs::read_to_string(&index_path) {
        Ok(content) => content,
        Err(e) => {
            eprintln!("Failed to read {}: {}", index_path.display(), e);
            return Err(Box::new(e));
        }
    };

    // Parse index.txt lines
    let assets: Vec<(String, String)> = index_content
        .lines()
        .filter_map(|line| {
            let parts: Vec<&str> = line.split('|').map(|s| s.trim()).collect();
            if parts.len() == 2 {
                Some((parts[0].to_string(), parts[1].to_string()))
            } else {
                None
            }
        })
        .collect();

    println!("Found {} assets for placeID {}", assets.len(), place_id);

    // Define concurrency for asset downloads per placeID
    let asset_concurrency = 10; // Adjust as needed

    // Process asset downloads concurrently
    stream::iter(assets)
        .for_each_concurrent(asset_concurrency, |(version, url)| {
            let client = client.clone();
            let base_dir = base_dir.clone();
            let place_id = place_id.to_string();
            async move {
                if let Err(e) = download_asset(&client, &base_dir, &place_id, &version, &url).await {
                    eprintln!("Failed to download asset {} for placeID {}: {}", url, place_id, e);
                }
            }
        })
        .await;

    println!("Completed placeID: {}", place_id);
    Ok(())
}

/// Downloads a single asset and saves it to the designated path
async fn download_asset(
    client: &Client,
    base_dir: &PathBuf,
    place_id: &str,
    version: &str,
    url: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    // Extract asset filename from URL
    let asset_filename = match url.split('/').last() {
        Some(name) => name,
        None => {
            return Err("Failed to extract asset filename from URL".into());
        }
    };

    // Define the target directory and file path
    let target_dir = base_dir.join(place_id).join(version);
    let target_path = target_dir.join(asset_filename);

    // Create directories if they don't exist
    create_dir_all(&target_dir).await?;

    // Check if the file already exists to avoid re-downloading
    if target_path.exists() {
        println!("Asset already exists: {:?}", target_path);
        return Ok(());
    }

    // Make the HTTP GET request
    let response = client.get(url).send().await?;

    if !response.status().is_success() {
        return Err(format!("Failed to download {}: HTTP {}", url, response.status()).into());
    }

    // Stream the response bytes to the file
    let mut file = File::create(&target_path).await?;
    let mut stream = response.bytes_stream();

    while let Some(chunk) = stream.next().await {
        let data = chunk?;
        file.write_all(&data).await?;
    }

    println!("Downloaded: {:?}", target_path);
    Ok(())
}

Make it so every asset it downloads ends with rbxl           e.x              + ".rbxl"