use std::{collections::HashMap, str::FromStr};

use cicero_core::target::{Target, Arch, OS};
use proc_macro2::{Span, TokenStream};
use quote::{TokenStreamExt, quote};
use syn::{Ident, Token, punctuated::{self, Punctuated}};

use crate::util;


pub(super) fn target_macro_implementation() -> TokenStream {

    let targets = Target::iter()
        .map(|target| {
            let identifier = util::convert_string_to_identifier(target.triple);
            (identifier, target)
        })
        .collect::<HashMap<_, _>>();

    template_module_definition(&targets)
}

fn template_module_definition(targets: &HashMap<Ident, Target>) -> TokenStream {

    let variants =
        targets.iter()
            .map(|(ident, target)| {
                let target_triple = target.triple;
                let target_arch = tokenize_arch(target.arch);
                let target_os = tokenize_os(target.os);

                let const_doc = format!("Meta-information for the target platform \"{target_triple}\".");

                quote! {
                    #[doc = #const_doc]
                    pub const #ident: ::cicero_core::target::Target =
                        ::cicero_core::target::construct_target_from_raw_parts(
                            #target_triple,
                            ::cicero_core::target::#target_arch,
                            ::cicero_core::target::#target_os,
                        );
                }
            })
            .collect::<TokenStream>();

    let all = targets.keys()
        .map(|ident| quote! {
            &#ident,
        })
        .collect::<TokenStream>();

    let all_count = targets.keys().count();

    quote! {
        /// Meta-information about the target platforms supported by the Rust compiler.
        pub mod target {
            /// Array of all target platforms supported by the Rust compiler.
            pub const ALL: [&::cicero_core::target::Target; #all_count] = [
                #all
            ];

            #variants
        }
    }
}

fn tokenize_arch(arch: Arch) -> TokenStream {
    match arch {
        Arch::AArch64 =>     quote!(Arch::AArch64),
        Arch::Amdgpu =>      quote!(Arch::Amdgpu),
        Arch::Arm =>         quote!(Arch::Arm),
        Arch::Arm64ec =>     quote!(Arch::Arm64ec),
        Arch::Avr =>         quote!(Arch::Avr),
        Arch::Bpf =>         quote!(Arch::Bpf),
        Arch::Csky =>        quote!(Arch::Csky),
        Arch::Hexagon =>     quote!(Arch::Hexagon),
        Arch::Loongarch32 => quote!(Arch::Loongarch32),
        Arch::Loongarch64 => quote!(Arch::Loongarch64),
        Arch::M68k =>        quote!(Arch::M68k),
        Arch::Mips =>        quote!(Arch::Mips),
        Arch::Mips32r6 =>    quote!(Arch::Mips32r6),
        Arch::Mips64 =>      quote!(Arch::Mips64),
        Arch::Mips64r6 =>    quote!(Arch::Mips64r6),
        Arch::Msp430 =>      quote!(Arch::Msp430),
        Arch::Nvptx64 =>     quote!(Arch::Nvptx64),
        Arch::PowerPc =>     quote!(Arch::PowerPc),
        Arch::PowerPc64 =>   quote!(Arch::PowerPc64),
        Arch::Riscv32 =>     quote!(Arch::Riscv32),
        Arch::Riscv64 =>     quote!(Arch::Riscv64),
        Arch::S390X =>       quote!(Arch::S390X),
        Arch::Sparc =>       quote!(Arch::Sparc),
        Arch::Sparc64 =>     quote!(Arch::Sparc64),
        Arch::Wasm32 =>      quote!(Arch::Wasm32),
        Arch::Wasm64 =>      quote!(Arch::Wasm64),
        Arch::X86 =>         quote!(Arch::X86),
        Arch::X86_64 =>      quote!(Arch::X86_64),
        Arch::Xtensa =>      quote!(Arch::Xtensa),
        other => unimplemented!("No tokenization implementation for Arch '{other}'. Please report this to the Cicero developers."),
    }
}

fn tokenize_os(os: OS) -> TokenStream {
    match os {
        OS::Aix =>        quote!(OS::Aix),
        OS::Amdhsa =>     quote!(OS::Amdhsa),
        OS::Android =>    quote!(OS::Android),
        OS::Cuda =>       quote!(OS::Cuda),
        OS::Cygwin =>     quote!(OS::Cygwin),
        OS::Dragonfly =>  quote!(OS::Dragonfly),
        OS::Emscripten => quote!(OS::Emscripten),
        OS::Espidf =>     quote!(OS::Espidf),
        OS::FreeBSD =>    quote!(OS::FreeBSD),
        OS::Fuchsia =>    quote!(OS::Fuchsia),
        OS::Haiku =>      quote!(OS::Haiku),
        OS::HelenOS =>    quote!(OS::HelenOS),
        OS::Hermit =>     quote!(OS::Hermit),
        OS::Horizon =>    quote!(OS::Horizon),
        OS::Hurd =>       quote!(OS::Hurd),
        OS::IllumOS =>    quote!(OS::IllumOS),
        OS::iOS =>        quote!(OS::iOS),
        OS::L4re =>       quote!(OS::L4re),
        OS::Linux =>      quote!(OS::Linux),
        OS::Lynxos178 =>  quote!(OS::Lynxos178),
        OS::MacOS =>      quote!(OS::MacOS),
        OS::Managarm =>   quote!(OS::Managarm),
        OS::Motor =>      quote!(OS::Motor),
        OS::NetBSD =>     quote!(OS::NetBSD),
        OS::None =>       quote!(OS::None),
        OS::Nto =>        quote!(OS::Nto),
        OS::Nuttx =>      quote!(OS::Nuttx),
        OS::OpenBSD =>    quote!(OS::OpenBSD),
        OS::Psp =>        quote!(OS::Psp),
        OS::Psx =>        quote!(OS::Psx),
        OS::Qurt =>       quote!(OS::Qurt),
        OS::Redox =>      quote!(OS::Redox),
        OS::Rtems =>      quote!(OS::Rtems),
        OS::Solaris =>    quote!(OS::Solaris),
        OS::SolidAsp3 =>  quote!(OS::SolidAsp3),
        OS::TeeOS =>      quote!(OS::TeeOS),
        OS::Trusty =>     quote!(OS::Trusty),
        OS::TvOS =>       quote!(OS::TvOS),
        OS::Uefi =>       quote!(OS::Uefi),
        OS::Unknown =>    quote!(OS::Unknown),
        OS::VexOS =>      quote!(OS::VexOS),
        OS::VisionOS =>   quote!(OS::VisionOS),
        OS::Vita =>       quote!(OS::Vita),
        OS::VxWorks =>    quote!(OS::VxWorks),
        OS::Wasi =>       quote!(OS::Wasi),
        OS::WatchOS =>    quote!(OS::WatchOS),
        OS::Windows =>    quote!(OS::Windows),
        OS::Xous =>       quote!(OS::Xous),
        OS::Zkvm =>       quote!(OS::Zkvm),
        other => unimplemented!("No tokenization implementation for OS '{other}'. Please report this to the Cicero developers."),
    }
}
