// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{Address, Bytecode, IdentifierWrapper};
use anyhow::{bail, format_err};
use aptos_types::{account_config::CORE_CODE_ADDRESS, event::EventKey, transaction::Module};
use move_deps::{
    move_binary_format::{
        access::ModuleAccess,
        file_format::{
            Ability, AbilitySet, CompiledModule, CompiledScript, StructTypeParameter, Visibility,
        },
    },
    move_core_types,
    move_core_types::{
        account_address::AccountAddress,
        identifier::Identifier,
        language_storage::{ModuleId, StructTag, TypeTag},
        parser::{parse_struct_tag, parse_type_tag},
        transaction_argument::TransactionArgument,
    },
    move_resource_viewer::{AnnotatedMoveStruct, AnnotatedMoveValue},
};

use poem_openapi::{Enum, Object, Union};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use std::{
    collections::BTreeMap,
    convert::{From, Into, TryFrom, TryInto},
    fmt,
    result::Result,
    str::FromStr,
};

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveResource {
    #[serde(rename = "type")]
    #[oai(rename = "type")]
    pub typ: MoveStructTag,
    pub data: MoveStructValue,
}

impl TryFrom<AnnotatedMoveStruct> for MoveResource {
    type Error = anyhow::Error;

    fn try_from(s: AnnotatedMoveStruct) -> anyhow::Result<Self> {
        Ok(Self {
            typ: s.type_.clone().into(),
            data: s.try_into()?,
        })
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Copy)]
pub struct U64(pub u64);

impl U64 {
    pub fn inner(&self) -> &u64 {
        &self.0
    }
}

impl From<u64> for U64 {
    fn from(d: u64) -> Self {
        Self(d)
    }
}

impl From<U64> for warp::http::header::HeaderValue {
    fn from(d: U64) -> Self {
        d.0.into()
    }
}

impl From<U64> for u64 {
    fn from(d: U64) -> Self {
        d.0
    }
}

impl From<U64> for move_core_types::value::MoveValue {
    fn from(d: U64) -> Self {
        move_core_types::value::MoveValue::U64(d.0)
    }
}

impl fmt::Display for U64 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.0)
    }
}

impl Serialize for U64 {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.0.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for U64 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = <String>::deserialize(deserializer)?;
        s.parse().map_err(D::Error::custom)
    }
}

impl FromStr for U64 {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let data = s.parse::<u64>().map_err(|e| {
            format_err!("Parsing u64 string {:?} failed, caused by error: {}", s, e)
        })?;

        Ok(U64(data))
    }
}

#[derive(Clone, Debug, Default, PartialEq, Copy)]
pub struct U128(pub u128);

impl U128 {
    pub fn inner(&self) -> &u128 {
        &self.0
    }
}

impl From<u128> for U128 {
    fn from(d: u128) -> Self {
        Self(d)
    }
}

impl From<U128> for u128 {
    fn from(d: U128) -> Self {
        d.0
    }
}

impl From<U128> for move_core_types::value::MoveValue {
    fn from(d: U128) -> Self {
        move_core_types::value::MoveValue::U128(d.0)
    }
}

impl Serialize for U128 {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.0.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for U128 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = <String>::deserialize(deserializer)?;
        let data = s.parse::<u128>().map_err(D::Error::custom)?;

        Ok(U128(data))
    }
}

impl FromStr for U128 {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let data = s.parse::<u128>().map_err(|e| {
            format_err!("Parsing u128 string {:?} failed, caused by error: {}", s, e)
        })?;

        Ok(U128(data))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct HexEncodedBytes(pub Vec<u8>);

impl HexEncodedBytes {
    pub fn json(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::to_value(self)?)
    }
}

impl FromStr for HexEncodedBytes {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> anyhow::Result<Self, anyhow::Error> {
        let hex_str = if let Some(hex) = s.strip_prefix("0x") {
            hex
        } else {
            s
        };
        Ok(Self(hex::decode(hex_str).map_err(|e| {
            format_err!(
                "decode hex-encoded string({:?}) failed, caused by error: {}",
                s,
                e
            )
        })?))
    }
}

impl fmt::Display for HexEncodedBytes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "0x{}", hex::encode(&self.0))?;
        Ok(())
    }
}

impl Serialize for HexEncodedBytes {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for HexEncodedBytes {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = <String>::deserialize(deserializer)?;
        s.parse().map_err(D::Error::custom)
    }
}

impl From<Vec<u8>> for HexEncodedBytes {
    fn from(bytes: Vec<u8>) -> Self {
        Self(bytes)
    }
}

impl From<HexEncodedBytes> for Vec<u8> {
    fn from(bytes: HexEncodedBytes) -> Self {
        bytes.0
    }
}

impl From<HexEncodedBytes> for move_core_types::value::MoveValue {
    fn from(d: HexEncodedBytes) -> Self {
        move_core_types::value::MoveValue::Vector(
            d.0.into_iter()
                .map(move_core_types::value::MoveValue::U8)
                .collect(),
        )
    }
}

impl TryFrom<HexEncodedBytes> for EventKey {
    type Error = anyhow::Error;
    fn try_from(bytes: HexEncodedBytes) -> anyhow::Result<Self> {
        Ok(bcs::from_bytes(&bytes.0)?)
    }
}

impl HexEncodedBytes {
    pub fn inner(&self) -> &[u8] {
        &self.0
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MoveStructValue(pub BTreeMap<IdentifierWrapper, serde_json::Value>);

impl TryFrom<AnnotatedMoveStruct> for MoveStructValue {
    type Error = anyhow::Error;
    fn try_from(s: AnnotatedMoveStruct) -> anyhow::Result<Self> {
        let mut map = BTreeMap::new();
        for (id, val) in s.value {
            map.insert(id.into(), MoveValue::try_from(val)?.json()?);
        }
        Ok(Self(map))
    }
}

#[derive(Clone, Debug, PartialEq, Union)]
pub enum MoveValue {
    U8(u8),
    U64(U64),
    U128(U128),
    Bool(bool),
    Address(Address),
    Vector(Vec<MoveValue>),
    Bytes(HexEncodedBytes),
    Struct(MoveStructValue),
    String(String),
}

impl MoveValue {
    pub fn json(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::to_value(self)?)
    }

    pub fn is_utf8_string(st: &StructTag) -> bool {
        st.address == CORE_CODE_ADDRESS
            && st.name.to_string() == "String"
            && st.module.to_string() == "string"
    }

    pub fn convert_utf8_string(v: AnnotatedMoveStruct) -> anyhow::Result<MoveValue> {
        if let Some((_, AnnotatedMoveValue::Bytes(bytes))) = v.value.into_iter().next() {
            Ok(MoveValue::String(String::from_utf8(bytes)?))
        } else {
            bail!("expect string::String, but failed to decode struct value");
        }
    }
}

impl TryFrom<AnnotatedMoveValue> for MoveValue {
    type Error = anyhow::Error;

    fn try_from(val: AnnotatedMoveValue) -> anyhow::Result<Self> {
        Ok(match val {
            AnnotatedMoveValue::U8(v) => MoveValue::U8(v),
            AnnotatedMoveValue::U64(v) => MoveValue::U64(U64(v)),
            AnnotatedMoveValue::U128(v) => MoveValue::U128(U128(v)),
            AnnotatedMoveValue::Bool(v) => MoveValue::Bool(v),
            AnnotatedMoveValue::Address(v) => MoveValue::Address(v.into()),
            AnnotatedMoveValue::Vector(_, vals) => MoveValue::Vector(
                vals.into_iter()
                    .map(MoveValue::try_from)
                    .collect::<anyhow::Result<_>>()?,
            ),
            AnnotatedMoveValue::Bytes(v) => MoveValue::Bytes(HexEncodedBytes(v)),
            AnnotatedMoveValue::Struct(v) => {
                if MoveValue::is_utf8_string(&v.type_) {
                    MoveValue::convert_utf8_string(v)?
                } else {
                    MoveValue::Struct(v.try_into()?)
                }
            }
        })
    }
}

impl From<TransactionArgument> for MoveValue {
    fn from(val: TransactionArgument) -> Self {
        match val {
            TransactionArgument::U8(v) => MoveValue::U8(v),
            TransactionArgument::U64(v) => MoveValue::U64(U64(v)),
            TransactionArgument::U128(v) => MoveValue::U128(U128(v)),
            TransactionArgument::Bool(v) => MoveValue::Bool(v),
            TransactionArgument::Address(v) => MoveValue::Address(v.into()),
            TransactionArgument::U8Vector(bytes) => MoveValue::Bytes(HexEncodedBytes(bytes)),
        }
    }
}

impl Serialize for MoveValue {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match &self {
            MoveValue::U8(v) => v.serialize(serializer),
            MoveValue::U64(v) => v.serialize(serializer),
            MoveValue::U128(v) => v.serialize(serializer),
            MoveValue::Bool(v) => v.serialize(serializer),
            MoveValue::Address(v) => v.serialize(serializer),
            MoveValue::Vector(v) => v.serialize(serializer),
            MoveValue::Bytes(v) => v.serialize(serializer),
            MoveValue::Struct(v) => v.serialize(serializer),
            MoveValue::String(v) => v.serialize(serializer),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MoveStructTag {
    pub address: Address,
    pub module: IdentifierWrapper,
    pub name: IdentifierWrapper,
    pub generic_type_params: Vec<MoveType>,
}

impl MoveStructTag {
    pub fn new(
        address: Address,
        module: IdentifierWrapper,
        name: IdentifierWrapper,
        generic_type_params: Vec<MoveType>,
    ) -> Self {
        Self {
            address,
            module,
            name,
            generic_type_params,
        }
    }
}

impl FromStr for MoveStructTag {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> anyhow::Result<Self, anyhow::Error> {
        Ok(parse_struct_tag(s)?.into())
    }
}

impl From<StructTag> for MoveStructTag {
    fn from(tag: StructTag) -> Self {
        Self {
            address: tag.address.into(),
            module: tag.module.into(),
            name: tag.name.into(),
            generic_type_params: tag.type_params.into_iter().map(MoveType::from).collect(),
        }
    }
}

impl fmt::Display for MoveStructTag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}::{}::{}", self.address, self.module, self.name)?;
        if let Some(first_ty) = self.generic_type_params.first() {
            write!(f, "<")?;
            write!(f, "{}", first_ty)?;
            for ty in self.generic_type_params.iter().skip(1) {
                write!(f, ", {}", ty)?;
            }
            write!(f, ">")?;
        }
        Ok(())
    }
}

impl Serialize for MoveStructTag {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for MoveStructTag {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let data = <String>::deserialize(deserializer)?;
        data.parse().map_err(D::Error::custom)
    }
}

impl TryFrom<MoveStructTag> for StructTag {
    type Error = anyhow::Error;
    fn try_from(tag: MoveStructTag) -> anyhow::Result<Self> {
        Ok(Self {
            address: tag.address.into(),
            module: tag.module.into(),
            name: tag.name.into(),
            type_params: tag
                .generic_type_params
                .into_iter()
                .map(|p| p.try_into())
                .collect::<anyhow::Result<Vec<TypeTag>>>()?,
        })
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MoveType {
    Bool,
    U8,
    U64,
    U128,
    Address,
    Signer,
    Vector { items: Box<MoveType> },
    Struct(MoveStructTag),
    GenericTypeParam { index: u16 },
    Reference { mutable: bool, to: Box<MoveType> },
    Unparsable(String),
}

impl MoveType {
    // Returns corresponding JSON data type for the value of `MoveType`
    pub fn json_type_name(&self) -> String {
        match self {
            MoveType::U8 => "integer".to_owned(),
            MoveType::U64 => "string<u64>".to_owned(),
            MoveType::U128 => "string<u128>".to_owned(),
            MoveType::Signer | MoveType::Address => "string<address>".to_owned(),
            MoveType::Bool => "boolean".to_owned(),
            MoveType::Vector { items } => {
                if matches!(**items, MoveType::U8) {
                    "string<hex>".to_owned()
                } else {
                    format!("array<{}>", items.json_type_name())
                }
            }
            MoveType::Struct(_) | MoveType::GenericTypeParam { index: _ } => {
                "string<move_struct_tag_id>".to_owned()
            }
            MoveType::Reference { mutable: _, to } => to.json_type_name(),
            MoveType::Unparsable(string) => string.to_string(),
        }
    }
}

impl fmt::Display for MoveType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MoveType::U8 => write!(f, "u8"),
            MoveType::U64 => write!(f, "u64"),
            MoveType::U128 => write!(f, "u128"),
            MoveType::Address => write!(f, "address"),
            MoveType::Signer => write!(f, "signer"),
            MoveType::Bool => write!(f, "bool"),
            MoveType::Vector { items } => write!(f, "vector<{}>", items),
            MoveType::Struct(s) => write!(f, "{}", s),
            MoveType::GenericTypeParam { index } => write!(f, "T{}", index),
            MoveType::Reference { mutable, to } => {
                if *mutable {
                    write!(f, "&mut {}", to)
                } else {
                    write!(f, "&{}", to)
                }
            }
            MoveType::Unparsable(string) => write!(f, "unparsable<{}>", string),
        }
    }
}

// This function cannot handle the full range of types that MoveType can
// represent. Internally, it uses parse_type_tag, which cannot handle references
// or generic type parameters. This function adds nominal support for references
// on top of parse_type_tag, but it still does not work for generic type params.
// For that, we have the Unparsable variant of MoveType, so the deserialization
// doesn't fail when dealing with these values.
impl FromStr for MoveType {
    type Err = anyhow::Error;

    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
        let mut is_ref = false;
        let mut is_mut = false;
        if s.starts_with('&') {
            s = &s[1..];
            is_ref = true;
        }
        if is_ref && s.starts_with("mut ") {
            s = &s[4..];
            is_mut = true;
        }
        // Previously this would just crap out, but this meant the API could
        // return a serialized version of an object and not be able to
        // deserialize it using that same object.
        let inner = match parse_type_tag(s) {
            Ok(inner) => inner.into(),
            Err(_e) => MoveType::Unparsable(s.to_string()),
        };
        if is_ref {
            Ok(MoveType::Reference {
                mutable: is_mut,
                to: Box::new(inner),
            })
        } else {
            Ok(inner)
        }
    }
}

impl Serialize for MoveType {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

// This deserialization has limitations, see the FromStr impl for MoveType.
impl<'de> Deserialize<'de> for MoveType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let data = <String>::deserialize(deserializer)
            .map_err(|e| D::Error::custom(format_err!("deserialize Move type failed, {}", e)))?;
        data.parse().map_err(D::Error::custom)
    }
}

impl MoveType {
    pub fn is_signer(&self) -> bool {
        match self {
            MoveType::Signer => true,
            MoveType::Reference { mutable: _, to } => to.is_signer(),
            _ => false,
        }
    }
}

impl From<TypeTag> for MoveType {
    fn from(tag: TypeTag) -> Self {
        match tag {
            TypeTag::Bool => MoveType::Bool,
            TypeTag::U8 => MoveType::U8,
            TypeTag::U64 => MoveType::U64,
            TypeTag::U128 => MoveType::U128,
            TypeTag::Address => MoveType::Address,
            TypeTag::Signer => MoveType::Signer,
            TypeTag::Vector(v) => MoveType::Vector {
                items: Box::new(MoveType::from(*v)),
            },
            TypeTag::Struct(v) => MoveType::Struct(v.into()),
        }
    }
}

impl TryFrom<MoveType> for TypeTag {
    type Error = anyhow::Error;
    fn try_from(tag: MoveType) -> anyhow::Result<Self> {
        let ret = match tag {
            MoveType::Bool => TypeTag::Bool,
            MoveType::U8 => TypeTag::U8,
            MoveType::U64 => TypeTag::U64,
            MoveType::U128 => TypeTag::U128,
            MoveType::Address => TypeTag::Address,
            MoveType::Signer => TypeTag::Signer,
            MoveType::Vector { items } => TypeTag::Vector(Box::new((*items).try_into()?)),
            MoveType::Struct(v) => TypeTag::Struct(v.try_into()?),
            _ => {
                return Err(anyhow::anyhow!(
                    "invalid move type for converting into `TypeTag`: {:?}",
                    &tag
                ))
            }
        };
        Ok(ret)
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveModule {
    pub address: Address,
    pub name: IdentifierWrapper,
    pub friends: Vec<MoveModuleId>,
    pub exposed_functions: Vec<MoveFunction>,
    pub structs: Vec<MoveStruct>,
}

impl From<CompiledModule> for MoveModule {
    fn from(m: CompiledModule) -> Self {
        let (address, name) = <(AccountAddress, Identifier)>::from(m.self_id());
        Self {
            address: address.into(),
            name: name.into(),
            friends: m
                .immediate_friends()
                .into_iter()
                .map(|f| f.into())
                .collect(),
            exposed_functions: m
                .function_defs
                .iter()
                .filter(|def| match def.visibility {
                    Visibility::Public | Visibility::Friend => true,
                    Visibility::Private => false,
                })
                .map(|def| m.new_move_function(def))
                .collect(),
            structs: m
                .struct_defs
                .iter()
                .map(|def| m.new_move_struct(def))
                .collect(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MoveModuleId {
    pub address: Address,
    pub name: IdentifierWrapper,
}

impl From<ModuleId> for MoveModuleId {
    fn from(id: ModuleId) -> Self {
        let (address, name) = <(AccountAddress, Identifier)>::from(id);
        Self {
            address: address.into(),
            name: name.into(),
        }
    }
}

impl From<MoveModuleId> for ModuleId {
    fn from(id: MoveModuleId) -> Self {
        ModuleId::new(id.address.into(), id.name.into())
    }
}

impl fmt::Display for MoveModuleId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}::{}", self.address, self.name)
    }
}

impl FromStr for MoveModuleId {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some((address, name)) = s.split_once("::") {
            return Ok(Self {
                address: address.parse().map_err(|_| invalid_move_module_id(s))?,
                name: name.parse().map_err(|_| invalid_move_module_id(s))?,
            });
        }
        Err(invalid_move_module_id(s))
    }
}

#[inline]
fn invalid_move_module_id(s: &str) -> anyhow::Error {
    format_err!("invalid Move module id: {}", s)
}

impl Serialize for MoveModuleId {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for MoveModuleId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let module_id = <String>::deserialize(deserializer)?;
        module_id.parse().map_err(D::Error::custom)
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveStruct {
    pub name: IdentifierWrapper,
    pub is_native: bool,
    pub abilities: Vec<MoveAbility>,
    pub generic_type_params: Vec<MoveStructGenericTypeParam>,
    pub fields: Vec<MoveStructField>,
}

// TODO: Consider finding a way to derive NewType here instead of using the
// custom macro, since some of the enum type information (such as the
// variants) is currently being lost.
#[derive(Clone, Debug, PartialEq)]
pub struct MoveAbility(pub Ability);

impl From<Ability> for MoveAbility {
    fn from(a: Ability) -> Self {
        Self(a)
    }
}

impl From<MoveAbility> for Ability {
    fn from(a: MoveAbility) -> Self {
        a.0
    }
}

impl fmt::Display for MoveAbility {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let st = match self.0 {
            Ability::Copy => "copy",
            Ability::Drop => "drop",
            Ability::Store => "store",
            Ability::Key => "key",
        };
        write!(f, "{}", st)
    }
}

impl FromStr for MoveAbility {
    type Err = anyhow::Error;

    fn from_str(ability: &str) -> Result<Self, Self::Err> {
        Ok(Self(match ability {
            "copy" => Ability::Copy,
            "drop" => Ability::Drop,
            "store" => Ability::Store,
            "key" => Ability::Key,
            _ => return Err(anyhow::anyhow!("invalid ability string: {}", ability)),
        }))
    }
}

impl Serialize for MoveAbility {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for MoveAbility {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let ability = <String>::deserialize(deserializer)?;
        ability.parse().map_err(D::Error::custom)
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveStructGenericTypeParam {
    pub constraints: Vec<MoveAbility>,
    #[oai(skip)]
    pub is_phantom: bool,
}

impl From<&StructTypeParameter> for MoveStructGenericTypeParam {
    fn from(param: &StructTypeParameter) -> Self {
        Self {
            constraints: param
                .constraints
                .into_iter()
                .map(MoveAbility::from)
                .collect(),
            is_phantom: param.is_phantom,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveStructField {
    pub name: IdentifierWrapper,
    #[serde(rename = "type")]
    #[oai(rename = "type")]
    pub typ: MoveType,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveFunction {
    pub name: IdentifierWrapper,
    pub visibility: MoveFunctionVisibility,
    pub is_entry: bool,
    pub generic_type_params: Vec<MoveFunctionGenericTypeParam>,
    pub params: Vec<MoveType>,
    #[serde(rename = "return")]
    #[oai(rename = "return")]
    pub return_: Vec<MoveType>,
}

impl From<&CompiledScript> for MoveFunction {
    fn from(script: &CompiledScript) -> Self {
        Self {
            name: Identifier::new("main").unwrap().into(),
            visibility: MoveFunctionVisibility::Public,
            is_entry: true,
            generic_type_params: script
                .type_parameters
                .iter()
                .map(MoveFunctionGenericTypeParam::from)
                .collect(),
            params: script
                .signature_at(script.parameters)
                .0
                .iter()
                .map(|s| script.new_move_type(s))
                .collect(),
            return_: vec![],
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Enum)]
#[serde(rename_all = "snake_case")]
#[oai(rename_all = "snake_case")]
pub enum MoveFunctionVisibility {
    Private,
    Public,
    Friend,
}

impl From<Visibility> for MoveFunctionVisibility {
    fn from(v: Visibility) -> Self {
        match &v {
            Visibility::Private => Self::Private,
            Visibility::Public => Self::Public,
            Visibility::Friend => Self::Friend,
        }
    }
}

impl From<MoveFunctionVisibility> for Visibility {
    fn from(v: MoveFunctionVisibility) -> Self {
        match &v {
            MoveFunctionVisibility::Private => Self::Private,
            MoveFunctionVisibility::Public => Self::Public,
            MoveFunctionVisibility::Friend => Self::Friend,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveFunctionGenericTypeParam {
    pub constraints: Vec<MoveAbility>,
}

impl From<&AbilitySet> for MoveFunctionGenericTypeParam {
    fn from(constraints: &AbilitySet) -> Self {
        Self {
            constraints: constraints.into_iter().map(MoveAbility::from).collect(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveModuleBytecode {
    pub bytecode: HexEncodedBytes,
    // We don't need deserialize MoveModule as it should be serialized
    // from `bytecode`.
    #[serde(skip_deserializing)]
    pub abi: Option<MoveModule>,
}

impl MoveModuleBytecode {
    pub fn new(bytes: Vec<u8>) -> Self {
        Self {
            bytecode: bytes.into(),
            abi: None,
        }
    }

    pub fn try_parse_abi(mut self) -> anyhow::Result<Self> {
        if self.abi.is_none() {
            // Ignore error, because it is possible a transaction module payload contains
            // invalid bytecode.
            // So we ignore the error and output bytecode without abi.
            if let Ok(module) = CompiledModule::deserialize(self.bytecode.inner()) {
                self.abi = Some(module.try_into()?);
            }
        }
        Ok(self)
    }
}

impl From<Module> for MoveModuleBytecode {
    fn from(m: Module) -> Self {
        Self::new(m.into_inner())
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)]
pub struct MoveScriptBytecode {
    pub bytecode: HexEncodedBytes,
    // We don't need deserialize MoveModule as it should be serialized
    // from `bytecode`.
    #[serde(skip_deserializing)]
    pub abi: Option<MoveFunction>,
}

impl MoveScriptBytecode {
    pub fn new(bytes: Vec<u8>) -> Self {
        Self {
            bytecode: bytes.into(),
            abi: None,
        }
    }

    pub fn try_parse_abi(mut self) -> Self {
        if self.abi.is_none() {
            // ignore error, because it is possible a transaction script payload contains
            // invalid bytecode.
            // So we ignore the error and output bytecode without abi.
            if let Ok(script) = CompiledScript::deserialize(self.bytecode.inner()) {
                self.abi = Some((&script).into());
            }
        }
        self
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct ScriptFunctionId {
    pub module: MoveModuleId,
    pub name: IdentifierWrapper,
}

impl FromStr for ScriptFunctionId {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some((module, name)) = s.rsplit_once("::") {
            return Ok(Self {
                module: module.parse().map_err(|_| invalid_script_function_id(s))?,
                name: name.parse().map_err(|_| invalid_script_function_id(s))?,
            });
        }
        Err(invalid_script_function_id(s))
    }
}

#[inline]
fn invalid_script_function_id(s: &str) -> anyhow::Error {
    format_err!("invalid script function id {:?}", s)
}

impl Serialize for ScriptFunctionId {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for ScriptFunctionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let script_fun_id = <String>::deserialize(deserializer)?;
        script_fun_id.parse().map_err(D::Error::custom)
    }
}

impl fmt::Display for ScriptFunctionId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}::{}", self.module, self.name)
    }
}

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

    use aptos_types::account_address::AccountAddress;
    use move_deps::{
        move_binary_format::file_format::AbilitySet,
        move_core_types::{
            identifier::Identifier,
            language_storage::{StructTag, TypeTag},
        },
        move_resource_viewer::{AnnotatedMoveStruct, AnnotatedMoveValue},
    };

    use serde::{de::DeserializeOwned, Serialize};
    use serde_json::{json, to_value, Value};
    use std::{boxed::Box, convert::TryFrom, fmt::Debug};

    #[test]
    fn test_serialize_move_type_tag() {
        use TypeTag::*;
        fn assert_serialize(t: TypeTag, expected: Value) {
            let value = to_value(MoveType::from(t)).unwrap();
            assert_json(value, expected)
        }
        assert_serialize(Bool, json!("bool"));
        assert_serialize(U8, json!("u8"));
        assert_serialize(U64, json!("u64"));
        assert_serialize(U128, json!("u128"));
        assert_serialize(Address, json!("address"));
        assert_serialize(Signer, json!("signer"));

        assert_serialize(Vector(Box::new(U8)), json!("vector<u8>"));

        assert_serialize(
            Struct(create_nested_struct()),
            json!("0x1::Home::ABC<address, 0x1::account::Base<u128, vector<u64>, vector<0x1::type::String>, 0x1::type::String>>"),
        );
    }

    #[test]
    fn test_serialize_move_resource() {
        use AnnotatedMoveValue::*;

        let res = MoveResource::try_from(annotated_move_struct(
            "Values",
            vec![
                (identifier("field_u8"), U8(7)),
                (identifier("field_u64"), U64(7)),
                (identifier("field_u128"), U128(7)),
                (identifier("field_bool"), Bool(true)),
                (identifier("field_address"), Address(address("0xdd"))),
                (
                    identifier("field_vector"),
                    Vector(TypeTag::U128, vec![U128(128)]),
                ),
                (identifier("field_bytes"), Bytes(vec![9, 9])),
                (
                    identifier("field_struct"),
                    Struct(annotated_move_struct(
                        "Nested",
                        vec![(
                            identifier("nested_vector"),
                            Vector(
                                TypeTag::Struct(type_struct("Host")),
                                vec![Struct(annotated_move_struct(
                                    "String",
                                    vec![
                                        (identifier("address1"), Address(address("0x0"))),
                                        (identifier("address2"), Address(address("0x123"))),
                                    ],
                                ))],
                            ),
                        )],
                    )),
                ),
            ],
        ))
        .unwrap();
        let value = to_value(&res).unwrap();
        assert_json(
            value,
            json!({
                "type": "0x1::type::Values",
                "data": {
                    "field_u8": 7,
                    "field_u64": "7",
                    "field_u128": "7",
                    "field_bool": true,
                    "field_address": "0xdd",
                    "field_vector": ["128"],
                    "field_bytes": "0x0909",
                    "field_struct": {
                        "nested_vector": [{"address1": "0x0", "address2": "0x123"}]
                    },
                }
            }),
        );
    }

    #[test]
    fn test_serialize_move_resource_with_address_0x0() {
        let res = MoveResource::try_from(annotated_move_struct(
            "Values",
            vec![(
                identifier("address_0x0"),
                AnnotatedMoveValue::Address(address("0x0")),
            )],
        ))
        .unwrap();
        let value = to_value(&res).unwrap();
        assert_json(
            value,
            json!({
                "type": "0x1::type::Values",
                "data": {
                    "address_0x0": "0x0",
                }
            }),
        );
    }

    #[test]
    fn test_serialize_deserialize_u64() {
        test_serialize_deserialize(U64::from(u64::MAX), json!(u64::MAX.to_string()))
    }

    #[test]
    fn test_serialize_deserialize_u128() {
        test_serialize_deserialize(U128::from(u128::MAX), json!(u128::MAX.to_string()))
    }

    #[test]
    fn test_serialize_deserialize_move_module_id() {
        test_serialize_deserialize(
            MoveModuleId {
                address: "0x1".parse().unwrap(),
                name: "Diem".parse().unwrap(),
            },
            json!("0x1::Diem"),
        );
    }

    #[test]
    fn test_parse_invalid_move_module_id_string() {
        assert_eq!(
            "invalid Move module id: 0x1",
            "0x1".parse::<MoveModuleId>().err().unwrap().to_string()
        );
        assert_eq!(
            "invalid Move module id: 0x1:",
            "0x1:".parse::<MoveModuleId>().err().unwrap().to_string()
        );
        assert_eq!(
            "invalid Move module id: 0x1:::",
            "0x1:::".parse::<MoveModuleId>().err().unwrap().to_string()
        );
        assert_eq!(
            "invalid Move module id: 0x1::???",
            "0x1::???"
                .parse::<MoveModuleId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid Move module id: Diem::Diem",
            "Diem::Diem"
                .parse::<MoveModuleId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid Move module id: 0x1::Diem::Diem",
            "0x1::Diem::Diem"
                .parse::<MoveModuleId>()
                .err()
                .unwrap()
                .to_string()
        );
    }

    #[test]
    fn test_serialize_deserialize_move_script_function_id() {
        test_serialize_deserialize(
            ScriptFunctionId {
                module: MoveModuleId {
                    address: "0x1".parse().unwrap(),
                    name: "Diem".parse().unwrap(),
                },
                name: "Add".parse().unwrap(),
            },
            json!("0x1::Diem::Add"),
        );
    }

    #[test]
    fn test_parse_invalid_move_script_function_id_string() {
        assert_eq!(
            "invalid script function id \"0x1\"",
            "0x1".parse::<ScriptFunctionId>().err().unwrap().to_string()
        );
        assert_eq!(
            "invalid script function id \"0x1:\"",
            "0x1:"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid script function id \"0x1:::\"",
            "0x1:::"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid script function id \"0x1::???\"",
            "0x1::???"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid script function id \"Diem::Diem\"",
            "Diem::Diem"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid script function id \"Diem::Diem::??\"",
            "Diem::Diem::??"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
        assert_eq!(
            "invalid script function id \"0x1::Diem::Diem::Diem\"",
            "0x1::Diem::Diem::Diem"
                .parse::<ScriptFunctionId>()
                .err()
                .unwrap()
                .to_string()
        );
    }

    #[test]
    fn test_serialize_deserialize_hex_encoded_bytes() {
        let bytes = hex::decode("abcd").unwrap();
        test_serialize_deserialize(HexEncodedBytes::from(bytes), json!("0xabcd"))
    }

    fn test_serialize_deserialize<O>(obj: O, expected: Value)
    where
        O: Serialize + DeserializeOwned + PartialEq + Debug,
    {
        let val = serde_json::to_value(&obj).unwrap();
        assert_eq!(val, expected);

        let data: O = serde_json::from_value(val).unwrap();
        assert_eq!(data, obj);
    }

    fn create_nested_struct() -> StructTag {
        let account = create_generic_type_struct();
        StructTag {
            address: address("0x1"),
            module: identifier("Home"),
            name: identifier("ABC"),
            type_params: vec![TypeTag::Address, TypeTag::Struct(account)],
        }
    }

    fn create_generic_type_struct() -> StructTag {
        StructTag {
            address: address("0x1"),
            module: identifier("account"),
            name: identifier("Base"),
            type_params: vec![
                TypeTag::U128,
                TypeTag::Vector(Box::new(TypeTag::U64)),
                TypeTag::Vector(Box::new(TypeTag::Struct(type_struct("String")))),
                TypeTag::Struct(type_struct("String")),
            ],
        }
    }

    fn type_struct(t: &str) -> StructTag {
        StructTag {
            address: address("0x1"),
            module: identifier("type"),
            name: identifier(t),
            type_params: vec![],
        }
    }

    fn address(hex: &str) -> AccountAddress {
        AccountAddress::from_hex_literal(hex).unwrap()
    }

    fn annotated_move_struct(
        typ: &str,
        values: Vec<(Identifier, AnnotatedMoveValue)>,
    ) -> AnnotatedMoveStruct {
        AnnotatedMoveStruct {
            abilities: AbilitySet::EMPTY,
            type_: type_struct(typ),
            value: values,
        }
    }

    fn identifier(id: &str) -> Identifier {
        Identifier::new(id).unwrap()
    }

    fn assert_json(ret: Value, expected: Value) {
        assert_eq!(
            &ret,
            &expected,
            "\nexpected: {}, \nbut got: {}",
            pretty(&expected),
            pretty(&ret)
        )
    }

    fn pretty(val: &Value) -> String {
        serde_json::to_string_pretty(val).unwrap()
    }
}
