use clap::{Parser, ValueEnum};
use log::info;
use serde::Deserialize;
use serde_json::{json, Value};
use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;

pub mod image_description;
mod pixelfed;

#[derive(Parser)]
#[command(name = "Pixelfed Image Bulk Uploader")]
#[command(version = "1.0")]
#[command(about = "Bulk uploads images to Pixelfed with image descriptions", long_about = None)]
#[command(version, about, long_about = None)]
struct Cli {
    /// Image description mode
    #[arg(short, long, default_value = "file")]
    mode: Mode,
    /// The title of the posting
    #[arg(short, long)]
    title: String,
    /// The path to the file to read
    #[arg(short, long)]
    image_path: String,
    /// Sets a custom config file
    #[arg(short, long, value_name = "FILE")]
    config: Option<String>,

    #[arg(short, long, value_name = "private")]
    visibility: Option<String>,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Mode {
    /// Use ChatGTP
    ChatGPT,
    /// Taking from a file
    File,
    /// Local LLM
    Local,
    // No Image Description
    None,
}

#[derive(Debug, Deserialize)]
struct Config {
    pixelfed_url: String,
    pixelfed_access_token: String,
    pixelfed_visibility: String, // Should be "unlisted"
    pixelfed_default_text: String,
    pixelfed_batch_size: usize,
    openai_api_key: String,
    openai_api_url: String,
    openai_model: String,
    ollama_api_key: String,
    ollama_api_url: String,
    ollama_model: String,
    caption_extension: String,
    prompt: String,
}

fn load_config(config_file: String) -> Result<Config, Box<dyn Error>> {
    //let config_str = fs::read_to_string("config.json")?;
    // Open the file in read-only mode with buffer.

    let file = File::open(PathBuf::from(config_file))?;
    let reader = BufReader::new(file);

    // Read the JSON contents of the file as an instance of `User`.

    let config: Config = serde_json::from_reader(reader)?;
    Ok(config)
}

// get all the JPEG files from the give directory
fn get_jpeg_files(directory: &str) -> Vec<String> {
    let mut images = Vec::new();
    if let Ok(entries) = fs::read_dir(directory) {
        for entry in entries.flatten() {
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext.eq_ignore_ascii_case("jpg") || ext.eq_ignore_ascii_case("jpeg") {
                    images.push(path.to_string_lossy().to_string());
                }
            }
        }
    }
    images
}

fn main() -> Result<(), Box<dyn Error>> {
    env_logger::init();

    let args = Cli::parse();

    //if args.len() < 2 || args.len() > 3 {
    //    eprintln!("Usage: {} <directory> [--title <title>]", args[0]);
    //    eprintln!("Usage: {} <directory> -ready [--title <title>]", args[0]);
    //    std::process::exit(1);
    //}

    let title = args.title;
    let my_config: String;

    match args.config {
        Some(configstring) => my_config = configstring,
        None => my_config = "config.json".to_string(),
    }
    info!("effective config file: {}", &my_config);

    let mut config = load_config(my_config).unwrap();

    info!("Config OK?: ");

    // Overwrite config values with command line arguments
    match args.visibility {
        Some(visibility) => config.pixelfed_visibility = visibility,
        None => {}
    }

    // get list of all the images in the gives path
    let images = get_jpeg_files(&args.image_path);
    info!("Images empty? {}", &images.is_empty().to_string());

    // knowing now the total number of images, calculate the number of batches
    let total_batches =
        (images.len() + config.pixelfed_batch_size - 1) / config.pixelfed_batch_size;
    println!(
        "Found a total of {} images to upload. Will take {} batches",
        &images.len(),
        &total_batches
    );

    // now iterate over all images in batches of batch_size
    for (i, chunk) in images.chunks(config.pixelfed_batch_size).enumerate() {
        info!("{}", i.clone());
        match pixelfed::bulk_upload_images(&config, chunk, i + 1, total_batches, &title, &args.mode)
        {
            Ok(_) => {
                println!("Upload of batch {} suceeded", &i + 1);
            }
            Err(e) => {
                println!("Upload of batch {} failed: {}", &i + 1, e.to_string());
            }
        };
    }
    // println!("All images uploaded successfully.");
    Ok(())
}
