use serde::Serialize;

pub const NATIVE_EMIT_SKELETON: &str = "native-emit-skeleton";
pub const NATIVE_EMIT_IMPLEMENTED: &str = "native-emit-implemented";
pub const NATIVE_EMIT_CONTRACT: &str = "native-emit-contract";
pub const HOST_TARGET_TRIPLE: &str = "aarch64-apple-darwin";

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct NativeEmitTargetStatus {
    pub triple: &'static str,
    pub format: &'static str,
    pub implemented: bool,
    pub stage: &'static str,
    pub reason_code: &'static str,
    pub artifact_kind: &'static str,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum NativeTargetKind {
    HostExecutable,
    Freestanding,
    RawBinary,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct NativeTarget {
    pub kind: NativeTargetKind,
    pub triple: String,
}

#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
const MACHO_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "aarch64-apple-darwin",
    format: "mach-o",
    implemented: true,
    stage: "owned-native-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "mach-o-executable",
};

#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
const MACHO_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "aarch64-apple-darwin",
    format: "mach-o",
    implemented: false,
    stage: "contract-only",
    reason_code: NATIVE_EMIT_CONTRACT,
    artifact_kind: "none",
};

const ELF_LINUX_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "x86_64-unknown-linux-gnu",
    format: "elf",
    implemented: true,
    stage: "owned-native-and-object-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "elf-relocatable-object+elf-executable",
};

const AARCH64_ELF_LINUX_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "aarch64-unknown-linux-gnu",
    format: "elf",
    implemented: true,
    stage: "owned-native-and-object-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "elf-relocatable-object+elf-executable",
};

const AARCH64_MACHO_STATICLIB_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "aarch64-apple-darwin",
    format: "mach-o",
    implemented: true,
    stage: "owned-object-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "mach-o-static-archive+app-bundle",
};

const ARM32_ELF_LINUX_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "armv7-unknown-linux-gnueabihf",
    format: "elf",
    implemented: true,
    stage: "owned-native-and-object-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "elf32-relocatable-object+elf32-executable",
};

const X86_64_WINDOWS_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "x86_64-pc-windows-msvc",
    format: "pe",
    implemented: true,
    stage: "owned-native-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "pe-executable",
};

const X86_64_FREESTANDING_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "x86_64-unknown-none",
    format: "elf",
    implemented: true,
    stage: "owned-object-subset-freestanding",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "elf-relocatable-object",
};

const WASM32_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: "wasm32-unknown-unknown",
    format: "wasm",
    implemented: true,
    stage: "owned-object-subset",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "wasm-module",
};

pub const AARCH64_NONE_TRIPLE: &str = "aarch64-unknown-none";

const AARCH64_NONE_TARGET: NativeEmitTargetStatus = NativeEmitTargetStatus {
    triple: AARCH64_NONE_TRIPLE,
    format: "elf",
    implemented: true,
    stage: "owned-object-subset-freestanding",
    reason_code: NATIVE_EMIT_IMPLEMENTED,
    artifact_kind: "elf-relocatable-object",
};

const NATIVE_EMIT_TARGETS: &[NativeEmitTargetStatus] = &[
    MACHO_TARGET,
    AARCH64_MACHO_STATICLIB_TARGET,
    X86_64_FREESTANDING_TARGET,
    AARCH64_NONE_TARGET,
    ELF_LINUX_TARGET,
    AARCH64_ELF_LINUX_TARGET,
    ARM32_ELF_LINUX_TARGET,
    X86_64_WINDOWS_TARGET,
    WASM32_TARGET,
];

pub fn all_native_emit_targets() -> &'static [NativeEmitTargetStatus] {
    NATIVE_EMIT_TARGETS
}

pub fn elf_linux_target_status() -> NativeEmitTargetStatus {
    ELF_LINUX_TARGET
}

pub fn macho_target_status() -> NativeEmitTargetStatus {
    MACHO_TARGET
}

pub fn resolve_native_target(triple: Option<&str>) -> NativeTarget {
    match triple.map(str::trim).filter(|value| !value.is_empty()) {
        Some(value) if value == "freestanding" || value.ends_with("-none") => NativeTarget {
            kind: NativeTargetKind::Freestanding,
            triple: if value == "freestanding" {
                "aarch64-none".to_string()
            } else {
                value.to_string()
            },
        },
        Some(value) if value == "raw" || value == "raw-binary" => NativeTarget {
            kind: NativeTargetKind::RawBinary,
            triple: "raw".to_string(),
        },
        Some(value) => NativeTarget {
            kind: NativeTargetKind::HostExecutable,
            triple: value.to_string(),
        },
        None => NativeTarget {
            kind: NativeTargetKind::HostExecutable,
            triple: HOST_TARGET_TRIPLE.to_string(),
        },
    }
}

pub fn freestanding_supported(target: &NativeTarget) -> bool {
    matches!(
        target.kind,
        NativeTargetKind::Freestanding | NativeTargetKind::RawBinary
    )
}

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

    #[test]
    fn registry_lists_macho_and_elf_targets() {
        let targets = all_native_emit_targets();
        assert_eq!(targets.len(), 9);
        assert_eq!(targets[0].format, "mach-o");
        assert_eq!(targets[1].artifact_kind, "mach-o-static-archive+app-bundle");
        assert_eq!(targets[2].triple, "x86_64-unknown-none");
        assert_eq!(targets[2].format, "elf");
        assert_eq!(targets[3].triple, "aarch64-unknown-none");
        assert_eq!(targets[3].format, "elf");
        assert_eq!(targets[4].triple, "x86_64-unknown-linux-gnu");
        assert_eq!(targets[4].format, "elf");
        assert_eq!(targets[5].triple, "aarch64-unknown-linux-gnu");
        assert_eq!(targets[6].triple, "armv7-unknown-linux-gnueabihf");
        assert_eq!(targets[7].triple, "x86_64-pc-windows-msvc");
        assert_eq!(targets[8].triple, "wasm32-unknown-unknown");
        assert_eq!(targets[8].format, "wasm");
    }

    #[test]
    fn reports_elf_linux_skeleton_status() {
        let status = elf_linux_target_status();
        assert!(status.implemented);
        assert_eq!(status.stage, "owned-native-and-object-subset");
        assert_eq!(status.reason_code, NATIVE_EMIT_IMPLEMENTED);
        assert_eq!(
            status.artifact_kind,
            "elf-relocatable-object+elf-executable"
        );
        assert_eq!(status.triple, "x86_64-unknown-linux-gnu");
    }

    #[test]
    fn default_target_is_host_triple() {
        let target = resolve_native_target(None);
        assert_eq!(target.kind, NativeTargetKind::HostExecutable);
        assert_eq!(target.triple, HOST_TARGET_TRIPLE);
    }

    #[test]
    fn freestanding_alias_resolves() {
        let target = resolve_native_target(Some("freestanding"));
        assert_eq!(target.kind, NativeTargetKind::Freestanding);
        assert_eq!(target.triple, "aarch64-none");
    }

    #[test]
    fn raw_binary_target_resolves() {
        let target = resolve_native_target(Some("raw"));
        assert_eq!(target.kind, NativeTargetKind::RawBinary);
        assert!(freestanding_supported(&target));
    }

    #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
    #[test]
    fn reports_macho_target_implemented_on_host() {
        let status = macho_target_status();
        assert!(status.implemented);
        assert_eq!(status.stage, "owned-native-subset");
        assert_eq!(status.reason_code, NATIVE_EMIT_IMPLEMENTED);
        assert_eq!(status.artifact_kind, "mach-o-executable");
    }

    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
    #[test]
    fn reports_macho_target_contract_off_host() {
        let status = macho_target_status();
        assert!(!status.implemented);
        assert_eq!(status.stage, "contract-only");
        assert_eq!(status.reason_code, NATIVE_EMIT_CONTRACT);
        assert_eq!(status.artifact_kind, "none");
    }
}
