use crate::boundary_emit;
use crate::boundary_ir::{BoundaryModule, IN_ABI_VERSION};
use crate::core_ir::UnifiedModule;
use crate::native_emit::NativeLinkage;
use crate::native_emit::coff::{COFF_WINDOWS_TRIPLE, write_exit_process_exe};
use crate::native_emit::elf::{
    AARCH64_LINUX_TRIPLE, ARMV7_LINUX_GNUEABIHF_TRIPLE, ELF_LINUX_TRIPLE, ElfExecutable, ElfObject,
    aarch64_linux_exit_code, aarch64_return_i32_object_code, arm32_linux_exit_code,
    arm32_return_i32_object_code, write_aarch64_executable, write_aarch64_relocatable_object,
    write_arm32_executable, write_arm32_relocatable_object, write_executable,
    write_x86_64_relocatable_object, x86_64_linux_exit_code, x86_64_return_i32_object_code,
};
use crate::native_emit::macho::{ExportSymbol, MachOImage, MachOLinkage, write_image};
use crate::native_emit::target::AARCH64_NONE_TRIPLE;
use crate::native_emit::wasm::{WASM32_UNKNOWN_TRIPLE, WasmModule, write_scalar_i32_module};
use crate::native_emit::x86_64_lower::{X86_64_TRIPLE, lower_module};

pub const NATIVE_OBJECT_SUBSET: &str = "native-object-subset";

pub struct NativeObjectRequest<'a> {
    pub target_triple: &'a str,
    pub linkage: NativeLinkage,
    pub entry: &'a str,
    pub exit_code: u8,
    pub module: &'a UnifiedModule,
    pub module_id: &'a str,
}

pub struct NativeObjectArtifact {
    pub bytes: Vec<u8>,
    pub artifact_kind: &'static str,
    pub backend_level: &'static str,
    pub runtime_level: &'static str,
    pub reason_code: &'static str,
    pub reason: &'static str,
    pub abi_manifest: Option<String>,
}

pub fn emit_native_object(request: &NativeObjectRequest<'_>) -> Option<NativeObjectArtifact> {
    match (request.target_triple, request.linkage) {
        (ELF_LINUX_TRIPLE, NativeLinkage::StaticLib) => Some(emit_x86_64_elf_object(request)),
        (ELF_LINUX_TRIPLE, NativeLinkage::Executable) => Some(emit_x86_64_elf_executable(request)),
        (COFF_WINDOWS_TRIPLE, NativeLinkage::Executable) => {
            Some(emit_x86_64_pe_executable(request))
        }
        (AARCH64_LINUX_TRIPLE, NativeLinkage::StaticLib) => Some(emit_aarch64_elf_object(request)),
        (AARCH64_LINUX_TRIPLE, NativeLinkage::Executable) => {
            Some(emit_aarch64_elf_executable(request))
        }
        ("aarch64-apple-darwin", NativeLinkage::StaticLib) => {
            Some(emit_aarch64_macho_archive(request))
        }
        (ARMV7_LINUX_GNUEABIHF_TRIPLE, NativeLinkage::StaticLib) => {
            Some(emit_arm32_elf_object(request))
        }
        (ARMV7_LINUX_GNUEABIHF_TRIPLE, NativeLinkage::Executable) => {
            Some(emit_arm32_elf_executable(request))
        }
        (WASM32_UNKNOWN_TRIPLE, NativeLinkage::StaticLib) => Some(emit_wasm32_module(request)),
        (X86_64_TRIPLE, NativeLinkage::StaticLib) => Some(emit_x86_64_freestanding_object(request)),
        (AARCH64_NONE_TRIPLE, NativeLinkage::StaticLib) => {
            Some(emit_aarch64_freestanding_object(request))
        }
        _ => None,
    }
}

fn emit_x86_64_pe_executable(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let mut bytes = Vec::new();
    write_exit_process_exe(request.exit_code, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "pe-executable",
        backend_level: "owned-native-subset-x86_64",
        runtime_level: "windows-exitprocess",
        reason_code: "native-x86_64-windows-exe-subset",
        reason: "inauguration owns PE32+ executable emission with kernel32 ExitProcess import for const-evaluable scalar entry functions on this target",
        abi_manifest: None,
    }
}

fn emit_x86_64_elf_object(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let object = ElfObject {
        code: x86_64_return_i32_object_code(request.exit_code),
        export_name: request.entry.to_string(),
    };
    let mut bytes = Vec::new();
    write_x86_64_relocatable_object(&object, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf-relocatable-object",
        backend_level: "owned-object-subset",
        runtime_level: "none",
        reason_code: NATIVE_OBJECT_SUBSET,
        reason: "inauguration owns ELF64 relocatable object emission for const-evaluable scalar entry functions on this target",
        abi_manifest: Some(object_abi_manifest(request)),
    }
}

fn emit_aarch64_elf_object(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let object = ElfObject {
        code: aarch64_return_i32_object_code(request.exit_code),
        export_name: request.entry.to_string(),
    };
    let mut bytes = Vec::new();
    write_aarch64_relocatable_object(&object, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf-relocatable-object",
        backend_level: "owned-object-subset",
        runtime_level: "none",
        reason_code: NATIVE_OBJECT_SUBSET,
        reason: "inauguration owns ELF64 AArch64 relocatable object emission for const-evaluable scalar entry functions on this target",
        abi_manifest: Some(object_abi_manifest(request)),
    }
}

fn emit_aarch64_elf_executable(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let exe = ElfExecutable {
        code: aarch64_linux_exit_code(request.exit_code),
        entry_offset: 0,
    };
    let mut bytes = Vec::new();
    write_aarch64_executable(&exe, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf-executable",
        backend_level: "owned-native-subset-aarch64",
        runtime_level: "linux-syscall-exit",
        reason_code: "native-aarch64-linux-exit-subset",
        reason: "inauguration owns ELF64 AArch64 Linux executable emission for const-evaluable scalar entry functions on this target",
        abi_manifest: None,
    }
}

fn emit_arm32_elf_object(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let object = ElfObject {
        code: arm32_return_i32_object_code(request.exit_code),
        export_name: request.entry.to_string(),
    };
    let mut bytes = Vec::new();
    write_arm32_relocatable_object(&object, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf32-relocatable-object",
        backend_level: "owned-object-subset",
        runtime_level: "none",
        reason_code: NATIVE_OBJECT_SUBSET,
        reason: "inauguration owns ELF32 ARM relocatable object emission for const-evaluable scalar entry functions on this target",
        abi_manifest: Some(object_abi_manifest(request)),
    }
}

fn emit_arm32_elf_executable(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let exe = ElfExecutable {
        code: arm32_linux_exit_code(request.exit_code),
        entry_offset: 0,
    };
    let mut bytes = Vec::new();
    write_arm32_executable(&exe, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf32-executable",
        backend_level: "owned-native-subset-arm32",
        runtime_level: "linux-syscall-exit",
        reason_code: "native-armv7-linux-exit-subset",
        reason: "inauguration owns ELF32 ARM Linux executable emission for const-evaluable scalar entry functions on this target",
        abi_manifest: None,
    }
}

fn emit_aarch64_macho_archive(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let image = MachOImage {
        code: aarch64_return_i32_object_code(request.exit_code),
        entry_offset: None,
        exports: vec![ExportSymbol {
            name: request.entry.to_string(),
            offset: 0,
        }],
    };
    let mut bytes = Vec::new();
    write_image(&image, MachOLinkage::StaticLib, request.entry, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "mach-o-static-archive",
        backend_level: "owned-object-subset",
        runtime_level: "none",
        reason_code: NATIVE_OBJECT_SUBSET,
        reason: "inauguration owns Mach-O static archive emission for const-evaluable scalar entry functions on this target",
        abi_manifest: Some(object_abi_manifest(request)),
    }
}

fn emit_x86_64_elf_executable(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let exe = ElfExecutable {
        code: x86_64_linux_exit_code(request.exit_code),
        entry_offset: 0,
    };
    let mut bytes = Vec::new();
    write_executable(&exe, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf-executable",
        backend_level: "owned-native-subset-x86_64",
        runtime_level: "linux-syscall-exit",
        reason_code: "native-x86_64-linux-exit-subset",
        reason: "inauguration owns ELF64 Linux executable emission for const-evaluable scalar entry functions on this target",
        abi_manifest: None,
    }
}

fn emit_aarch64_freestanding_object(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let object = ElfObject {
        code: aarch64_return_i32_object_code(request.exit_code),
        export_name: request.entry.to_string(),
    };
    let mut bytes = Vec::new();
    write_aarch64_relocatable_object(&object, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "elf-relocatable-object",
        backend_level: "owned-object-subset-freestanding",
        runtime_level: "freestanding-none",
        reason_code: "native-aarch64-unknown-none-freestanding",
        reason: "inauguration owns ELF64 AArch64 relocatable object emission for const-evaluable scalar entry functions on freestanding target",
        abi_manifest: Some(object_abi_manifest(request)),
    }
}

fn emit_x86_64_freestanding_object(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    // Use real x86_64 lowering for freestanding targets
    match lower_module(request.module, request.entry) {
        Ok(result) => {
            let mut bytes = Vec::new();
            let object = ElfObject {
                code: result.code,
                export_name: request.entry.to_string(),
            };
            write_x86_64_relocatable_object(&object, &mut bytes);
            NativeObjectArtifact {
                bytes,
                artifact_kind: "elf-relocatable-object",
                backend_level: "owned-native-subset-freestanding",
                runtime_level: "freestanding-none",
                reason_code: "native-x86_64-unknown-none-freestanding",
                reason: "inauguration owns ELF64 relocatable object emission for real x86_64 freestanding function bodies",
                abi_manifest: Some(object_abi_manifest(request)),
            }
        }
        Err(_err) => {
            // Fallback to const-evaluated exit code for trivial functions
            let object = ElfObject {
                code: x86_64_return_i32_object_code(request.exit_code),
                export_name: request.entry.to_string(),
            };
            let mut bytes = Vec::new();
            write_x86_64_relocatable_object(&object, &mut bytes);
            NativeObjectArtifact {
                bytes,
                artifact_kind: "elf-relocatable-object",
                backend_level: "owned-object-subset",
                runtime_level: "freestanding-none",
                reason_code: "native-object-subset",
                reason: "inauguration owns ELF64 relocatable object emission for const-evaluable scalar entry functions on freestanding target",
                abi_manifest: Some(object_abi_manifest(request)),
            }
        }
    }
}

fn emit_wasm32_module(request: &NativeObjectRequest<'_>) -> NativeObjectArtifact {
    let module = WasmModule {
        export_name: request.entry.to_string(),
        value: request.exit_code,
    };
    let mut bytes = Vec::new();
    write_scalar_i32_module(&module, &mut bytes);
    NativeObjectArtifact {
        bytes,
        artifact_kind: "wasm-module",
        backend_level: "owned-object-subset",
        runtime_level: "none",
        reason_code: NATIVE_OBJECT_SUBSET,
        reason: "inauguration owns WebAssembly module emission for const-evaluable scalar entry functions on this target",
        abi_manifest: None,
    }
}

fn object_abi_manifest(request: &NativeObjectRequest<'_>) -> String {
    boundary_emit::emit_abi_manifest_with_package(
        &BoundaryModule {
            abi_version: IN_ABI_VERSION,
            module: request
                .module
                .effective_module_id(request.module_id)
                .to_string(),
            layouts: Vec::new(),
            symbols: Vec::new(),
            allocators: Vec::new(),
            layout_hash: String::new(),
        }
        .with_layout_hash(),
        request.module.identity.package.as_deref(),
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core_ir::UnifiedModule;

    #[test]
    fn dispatches_x86_64_staticlib_object() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: ELF_LINUX_TRIPLE,
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("object artifact");
        assert_eq!(artifact.artifact_kind, "elf-relocatable-object");
        assert_eq!(artifact.reason_code, NATIVE_OBJECT_SUBSET);
        assert!(artifact.bytes.windows(6).any(|window| window == b"answer"));
        assert!(artifact.abi_manifest.is_some());
    }

    #[test]
    fn dispatches_wasm32_staticlib_module() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: WASM32_UNKNOWN_TRIPLE,
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("wasm artifact");
        assert_eq!(artifact.artifact_kind, "wasm-module");
        assert_eq!(artifact.reason_code, NATIVE_OBJECT_SUBSET);
        assert!(artifact.bytes.windows(6).any(|window| window == b"answer"));
        assert!(artifact.abi_manifest.is_none());
    }

    #[test]
    fn ignores_unsupported_target() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: "riscv64gc-unknown-none-elf",
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        assert!(emit_native_object(&request).is_none());
    }

    #[test]
    fn dispatches_aarch64_linux_staticlib_object() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: "aarch64-unknown-linux-gnu",
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("aarch64 object artifact");
        assert_eq!(artifact.artifact_kind, "elf-relocatable-object");
        assert_eq!(artifact.reason_code, NATIVE_OBJECT_SUBSET);
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[18], artifact.bytes[19]]),
            183
        );
    }

    #[test]
    fn dispatches_aarch64_apple_darwin_staticlib_archive() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: "aarch64-apple-darwin",
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("macho archive artifact");
        assert_eq!(artifact.artifact_kind, "mach-o-static-archive");
        assert_eq!(artifact.reason_code, NATIVE_OBJECT_SUBSET);
        assert_eq!(&artifact.bytes[..8], b"!<arch>\n");
        assert!(artifact.bytes.windows(7).any(|window| window == b"_answer"));
    }

    #[test]
    fn dispatches_arm32_staticlib_object() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: "armv7-unknown-linux-gnueabihf",
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("arm32 object artifact");
        assert_eq!(artifact.artifact_kind, "elf32-relocatable-object");
        assert_eq!(artifact.reason_code, NATIVE_OBJECT_SUBSET);
        assert_eq!(artifact.bytes[4], 1);
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[18], artifact.bytes[19]]),
            40
        );
    }

    #[test]
    fn dispatches_aarch64_freestanding_object() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: AARCH64_NONE_TRIPLE,
            linkage: NativeLinkage::StaticLib,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("aarch64 freestanding artifact");
        assert_eq!(artifact.artifact_kind, "elf-relocatable-object");
        assert_eq!(
            artifact.reason_code,
            "native-aarch64-unknown-none-freestanding"
        );
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[18], artifact.bytes[19]]),
            183
        );
    }

    #[test]
    fn dispatches_x86_64_linux_executable() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: ELF_LINUX_TRIPLE,
            linkage: NativeLinkage::Executable,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("x86 executable artifact");
        assert_eq!(artifact.artifact_kind, "elf-executable");
        assert_eq!(artifact.reason_code, "native-x86_64-linux-exit-subset");
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[16], artifact.bytes[17]]),
            2
        );
    }

    #[test]
    fn dispatches_aarch64_linux_executable() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: AARCH64_LINUX_TRIPLE,
            linkage: NativeLinkage::Executable,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("aarch64 executable artifact");
        assert_eq!(artifact.artifact_kind, "elf-executable");
        assert_eq!(artifact.reason_code, "native-aarch64-linux-exit-subset");
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[18], artifact.bytes[19]]),
            183
        );
    }

    #[test]
    fn dispatches_arm32_linux_executable() {
        let module = UnifiedModule::new(Vec::new());
        let request = NativeObjectRequest {
            target_triple: ARMV7_LINUX_GNUEABIHF_TRIPLE,
            linkage: NativeLinkage::Executable,
            entry: "answer",
            exit_code: 42,
            module: &module,
            module_id: "App",
        };
        let artifact = emit_native_object(&request).expect("arm32 executable artifact");
        assert_eq!(artifact.artifact_kind, "elf32-executable");
        assert_eq!(artifact.reason_code, "native-armv7-linux-exit-subset");
        assert_eq!(artifact.bytes[4], 1);
        assert_eq!(
            u16::from_le_bytes([artifact.bytes[18], artifact.bytes[19]]),
            40
        );
    }
}
