use crate::api::download::download_and_export_with_json_header;
use crate::api::search::search_messages;
use crate::api::stats::fetch_account_stats;
use crate::BichonCliConfig;
use bichon_core::account::payload::MinimalAccount;
use console::style;
use dialoguer::Confirm;
use dialoguer::{theme::ColorfulTheme, Input};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::Client;
use std::path::{Path, PathBuf};
use sysinfo::Disks;

pub async fn handle_account_export(
    config: &BichonCliConfig,
    account: MinimalAccount,
    theme: &ColorfulTheme,
) {
    let client = Client::new();

    println!("Fetching account statistics...");
    let stats = match fetch_account_stats(&client, config, account.id).await {
        Some(s) => s,
        None => {
            eprintln!("{} Failed to fetch account statistics.", style("✘").red());
            return;
        }
    };

    println!("\n--- Account Statistics ---");
    println!("  Total Emails: {}", style(stats.total_count).cyan());
    println!(
        "  Total Size:   {}",
        style(format_bytes(stats.total_size)).cyan()
    );

    let path = loop {
        let input: String = Input::with_theme(theme)
            .with_prompt("Enter ABSOLUTE directory path for MBOX file")
            .interact_text()
            .unwrap();

        let p = PathBuf::from(&input);

        if !p.is_absolute() {
            eprintln!(
                " {} {}",
                style("✘").red(),
                style("Invalid path: Must be an absolute path.").red()
            );
            continue;
        }

        if !p.exists() {
            eprintln!(
                " {} {}",
                style("✘").red(),
                style("Invalid path: Directory does not exist.").red()
            );
            continue;
        }

        if !p.is_dir() {
            eprintln!(
                " {} {}",
                style("✘").red(),
                style("Invalid path: The path provided is not a directory.").red()
            );
            continue;
        }
        break p;
    };

    let disks = Disks::new_with_refreshed_list();
    let disk_result = disks
        .list()
        .iter()
        .find(|d| path.starts_with(d.mount_point()))
        .ok_or_else(|| "Could not identify the disk for the provided path.");

    match disk_result {
        Ok(disk) => {
            let free_space = disk.available_space();
            let required_space = (stats.total_size as f64 * 1.2) as u64;

            if free_space < required_space {
                eprintln!(
                " {} Insufficient disk space (including 10% safety buffer)!\n   Required:  {} (Base: {})\n   Available: {}",
                style("✘").red(),
                style(format_bytes(required_space)).yellow(),
                style(format_bytes(stats.total_size)).yellow(),
                style(format_bytes(free_space)).yellow()
            );
                return;
            }

            println!(
                " {} Disk space check passed. (Required: {}, Available: {})",
                style("✔").green(),
                style(format_bytes(required_space)).cyan(),
                style(format_bytes(free_space)).cyan()
            );
        }
        Err(e) => {
            eprintln!(" {} {}", style("✘").red(), style(e).red());
            return;
        }
    }
    let mbox_file = get_unique_mbox_path(&path, account.id, &account.email);
    if Confirm::with_theme(theme)
        .with_prompt(format!(
            "Export {} emails to '{}'?",
            stats.total_count,
            mbox_file.display()
        ))
        .default(true)
        .interact()
        .unwrap()
    {
        println!(
            " {} Starting export ({} items per page)...",
            style("✔").green(),
            100
        );

        let pb = ProgressBar::new(stats.total_count as u64);
        pb.set_style(ProgressStyle::with_template(
            "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) {msg}"
        ).unwrap());

        let mut file = match tokio::fs::OpenOptions::new()
            .append(true)
            .create(true)
            .open(&mbox_file)
            .await
        {
            Ok(f) => f,
            Err(e) => {
                eprintln!(" ✘ Failed to open file '{}': {}", path.display(), e);
                return;
            }
        };

        let page_size = 100;
        let mut current_page = 1;
        let mut total_pages;

        loop {
            if let Some(batch) = search_messages(&client, config, current_page, page_size).await {
                total_pages = batch.total_pages.unwrap();

                pb.set_message(format!("Page {}/{}", current_page, total_pages));

                for envelope in batch.items {
                    let success =
                        download_and_export_with_json_header(&client, config, envelope, &mut file)
                            .await;

                    if !success {
                        pb.finish_with_message("Failed");
                        eprintln!(" ✘ Failed to export an email. Aborting process...");
                        return;
                    }
                    pb.inc(1);
                }
                if current_page >= total_pages {
                    break;
                }
                current_page += 1;
            } else {
                pb.finish_with_message("Error");
                eprintln!(
                    " ✘ Failed to fetch page {}. Aborting process...",
                    current_page
                );
                return;
            }
        }
        pb.finish();
        println!(" {} Export complete!", style("✔").green());
    }
}

fn format_bytes(bytes: u64) -> String {
    if bytes < 1024 {
        format!("{:.2} B", bytes)
    } else if bytes < 1024 * 1024 {
        format!("{:.2} KB", bytes / 1024)
    } else if bytes < 1024 * 1024 * 1024 {
        format!("{:.2} MB", bytes / 1024 / 1024)
    } else {
        format!("{:.2} GB", bytes / 1024 / 1024 / 1024)
    }
}

fn get_unique_mbox_path(base_dir: &Path, account_id: u64, email: &str) -> PathBuf {
    let email_part = email.replace(' ', "_");

    let mut base_name = format!("account_{}_{}", account_id, email_part);
    if base_name.starts_with('.') {
        base_name = format!("_{}", base_name);
    }

    let mut final_path = base_dir.join(format!("{}.mbox", base_name));

    let mut counter = 1;
    while final_path.exists() {
        final_path = base_dir.join(format!("{}_{}.mbox", base_name, counter));
        counter += 1;
    }
    final_path
}
