use crate::ScriptManifest;
use crate::generator::Generator;
use chrono::Utc;
use std::collections::HashMap;
use std::fmt::Write;

pub struct KotlinJVMGenerator;

impl Generator for KotlinJVMGenerator {
    fn generate(&self, manifest: &ScriptManifest) -> anyhow::Result<String> {
        let mut output = String::new();

        let local_time = Utc::now();

        writeln!(
            output,
            "// Auto-generated by dropbear-engine with the magna-carta parser on {} UTC",
            local_time.format("%Y-%m-%d %H:%M:%S")
        )?;
        writeln!(output)?;

        writeln!(output, "package com.dropbear.decl")?;
        writeln!(output)?;

        let mut imported_packages = std::collections::HashSet::new();
        for item in manifest.items() {
            if let Some(last_dot) = item.fqcn().rfind('.') {
                let package = &item.fqcn()[..last_dot];
                imported_packages.insert(package.to_string());
            }
        }

        for package in &imported_packages {
            writeln!(output, "import {}.*", package)?;
        }
        writeln!(output)?;

        let mut tag_map: HashMap<String, Vec<String>> = HashMap::new();
        for item in manifest.items() {
            let simple_name = item.simple_name();
            if item.tags().is_empty() {
                continue;
            }
            for tag in item.tags() {
                tag_map
                    .entry(tag.clone())
                    .or_default()
                    .push(simple_name.to_string());
            }
        }

        writeln!(output, "object RunnableRegistry {{")?;
        writeln!(
            output,
            "    private val tagRegistry = mutableMapOf<String, MutableList<() -> com.dropbear.ecs.System>>()"
        )?;
        writeln!(output)?;

        writeln!(output, "    init {{")?;
        writeln!(output, "        registerStaticScripts()")?;
        writeln!(output, "    }}")?;
        writeln!(output)?;

        writeln!(output, "    private fun registerStaticScripts() {{")?;
        for (tag, classes) in &tag_map {
            writeln!(output, "        // Tag: {}", tag)?;
            for class in classes {
                writeln!(
                    output,
                    "        tagRegistry.computeIfAbsent(\"{}\") {{ mutableListOf() }}.add(::{})",
                    tag, class
                )?;
            }
        }
        writeln!(output, "    }}")?;
        writeln!(output)?;

        writeln!(output, "    @Synchronized")?;
        writeln!(output, "    fun reload() {{")?;
        writeln!(output, "        tagRegistry.clear()")?;
        writeln!(output, "        registerStaticScripts()")?;
        writeln!(output, "    }}")?;
        writeln!(output)?;

        writeln!(
            output,
            "    fun getScriptFactories(tag: String): List<() -> com.dropbear.ecs.System> {{"
        )?;
        writeln!(
            output,
            "        return tagRegistry[tag]?.toList() ?: emptyList()"
        )?;
        writeln!(output, "    }}")?;
        writeln!(output)?;

        writeln!(
            output,
            "    fun instantiateScripts(tag: String): List<com.dropbear.ecs.System> {{"
        )?;
        writeln!(
            output,
            "        return getScriptFactories(tag).map {{ it() }}"
        )?;
        writeln!(output, "    }}")?;
        writeln!(output)?;

        writeln!(output, "    fun getAllTags(): Set<String> {{")?;
        writeln!(output, "        return tagRegistry.keys.toSet()")?;
        writeln!(output, "    }}")?;

        writeln!(output, "}}")?;

        writeln!(output)?;
        writeln!(output, "object ComponentManager {{")?;
        writeln!(
            output,
            "    private val instances: MutableMap<String, com.dropbear.ecs.NativeComponent> = mutableMapOf()"
        )?;
        writeln!(output)?;
        writeln!(
            output,
            "    private fun registerOne(component: com.dropbear.ecs.NativeComponent) {{"
        )?;
        writeln!(output, "        component.register()")?;
        writeln!(
            output,
            "        instances[component.fullyQualifiedTypeName] = component"
        )?;
        writeln!(output, "    }}")?;
        writeln!(output)?;
        writeln!(output, "    @Synchronized")?;
        writeln!(output, "    fun registerAll() {{")?;
        for component in manifest.components() {
            writeln!(
                output,
                "        registerOne({simple}())",
                simple = component.simple_name(),
            )?;
        }
        writeln!(output, "    }}")?;
        writeln!(output)?;
        writeln!(
            output,
            "    fun updateKotlinComponent(fqcn: String, entityId: Long, dt: Double) {{"
        )?;
        writeln!(output, "        val instance = instances[fqcn] ?: return")?;
        writeln!(
            output,
            "        val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)"
        )?;
        writeln!(output, "        instance.setCurrentEntity(entityId)")?;
        writeln!(output, "        instance.updateComponent(engine, dt)")?;
        writeln!(output, "        instance.clearCurrentEntity()")?;
        writeln!(output, "    }}")?;
        writeln!(output)?;
        writeln!(output, "    fun inspectKotlinComponent(fqcn: String) {{")?;
        writeln!(output, "        val instance = instances[fqcn] ?: return")?;
        writeln!(
            output,
            "        val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)"
        )?;
        writeln!(output, "        instance.inspect(engine)")?;
        writeln!(output, "    }}")?;
        writeln!(output, "}}")?;

        Ok(output)
    }
}
