use anyhow::Result;
use std::fs;
use std::path::Path;

use crate::config::{Config, ProductType};
use crate::sync::Mapping;

pub fn write_output(config: &Config, mapping: &Mapping) -> Result<()> {
    let output = match &config.output {
        Some(o) => o,
        None => {
            println!("No output configured, skipping code generation");
            return Ok(());
        }
    };

    let lua_content = generate_lua(config, mapping);
    write_file(&output.path, &lua_content)?;
    println!("Generated Lua output: {}", output.path);

    if output.typescript {
        let dts_path = output.path.replace(".luau", ".d.ts").replace(".lua", ".d.ts");
        let dts_content = generate_dts(config, mapping);
        write_file(&dts_path, &dts_content)?;
        println!("Generated TypeScript definitions: {}", dts_path);
    }

    Ok(())
}

fn write_file(path: &str, content: &str) -> Result<()> {
    let path = Path::new(path);

    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }

    fs::write(path, content)?;
    Ok(())
}

fn generate_lua(config: &Config, mapping: &Mapping) -> String {
    let mut dev_products = Vec::new();
    let mut gamepasses = Vec::new();

    for (key, product) in &config.products {
        let roblox_id = product
            .product_id
            .or_else(|| mapping.get(key).map(|m| m.roblox_id));

        if let Some(id) = roblox_id {
            match product.product_type {
                ProductType::DevProduct => {
                    dev_products.push((key.clone(), id));
                }
                ProductType::Gamepass => {
                    gamepasses.push((key.clone(), id));
                }
            }
        }
    }

    dev_products.sort_by(|a, b| a.0.cmp(&b.0));
    gamepasses.sort_by(|a, b| a.0.cmp(&b.0));

    let mut output = String::new();
    output.push_str("-- This file is auto-generated by spearmint. Do not edit manually.\n\n");
    output.push_str("local Products = {\n");

    output.push_str("\tDevProducts = {\n");
    for (key, id) in &dev_products {
        output.push_str(&format!("\t\t[\"{}\"] = {},\n", key, id));
    }
    output.push_str("\t},\n");

    output.push_str("\tGamepasses = {\n");
    for (key, id) in &gamepasses {
        output.push_str(&format!("\t\t[\"{}\"] = {},\n", key, id));
    }
    output.push_str("\t},\n");

    output.push_str("}\n\n");
    output.push_str("return Products\n");

    output
}

fn generate_dts(config: &Config, mapping: &Mapping) -> String {
    let mut dev_products = Vec::new();
    let mut gamepasses = Vec::new();

    for (key, product) in &config.products {
        let roblox_id = product
            .product_id
            .or_else(|| mapping.get(key).map(|m| m.roblox_id));

        if let Some(id) = roblox_id {
            match product.product_type {
                ProductType::DevProduct => {
                    dev_products.push((key.clone(), id));
                }
                ProductType::Gamepass => {
                    gamepasses.push((key.clone(), id));
                }
            }
        }
    }

    dev_products.sort_by(|a, b| a.0.cmp(&b.0));
    gamepasses.sort_by(|a, b| a.0.cmp(&b.0));

    let mut output = String::new();
    output.push_str("// This file is auto-generated by spearmint. Do not edit manually.\n\n");
    output.push_str("interface Products {\n");

    output.push_str("\tDevProducts: {\n");
    for (key, id) in &dev_products {
        output.push_str(&format!("\t\treadonly \"{}\": {};\n", key, id));
    }
    output.push_str("\t};\n");

    output.push_str("\tGamepasses: {\n");
    for (key, id) in &gamepasses {
        output.push_str(&format!("\t\treadonly \"{}\": {};\n", key, id));
    }
    output.push_str("\t};\n");

    output.push_str("}\n\n");
    output.push_str("declare const Products: Products;\n");
    output.push_str("export = Products;\n");

    output
}
