use cosmwasm_std::{
    coin,
    testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR},
    to_binary, Binary, Coin, ContractResult, CosmosMsg, Event, ReplyOn, Response, SystemError,
    SystemResult, Uint128, WasmMsg, WasmQuery,
};
use cw_token_bridge::msg::TransferInfoResponse;
use ibc_translator::{
    execute::{
        complete_transfer_and_convert, contract_addr_from_base58, convert_and_transfer,
        parse_bank_token_factory_contract, submit_update_chain_to_channel_map, TransferType,
    },
    msg::COMPLETE_TRANSFER_REPLY_ID,
    state::{CURRENT_TRANSFER, CW_DENOMS, TOKEN_BRIDGE_CONTRACT},
};
use wormhole_bindings::tokenfactory::{TokenFactoryMsg, TokenMsg};

mod test_setup;
use test_setup::{
    execute_custom_mock_deps, mock_env_custom_contract, WORMHOLE_CONTRACT_ADDR, WORMHOLE_USER_ADDR,
};

// Tests
// 1. complete_transfer_and_convert
//    1. happy path
//    2. no token bridge state
//    3. failure transferinfo query
//    4. failure humanize recipient
//    5. no match recipient contract
// 2. convert_and_transfer
//    1. happy path
//    2. no token bridge state
//    3. no funds
//    4. too many funds
//    5. parse method failure
// 3. parse_bank_token_factory_contract
//    1. happy path
//    2. failure denom length
//    3. failure non factory token
//    4. failure non contract created
//    5. failure base58 decode failure
//    6. failure no storage
//    7. failure storage mismatch
// 4. contract_addr_from_base58
//    1. happy path
//    2. failure decode base58
// 5. submit_update_chain_to_channel_map
//    1. happy path
//    2. failed to parse vaa
//    3. unsupported VAA version
//    4. not a governance vaa
//    5. failed to parse governance packet
//    6. governance vaa is for another chain
//    7. governance vaa already executed
//    8. chain is for wormchain
//    9. failed to parse channel-id

// TESTS: complete_transfer_and_convert
// 1. Happy path
#[test]
fn complete_transfer_and_convert_happy_path() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env_custom_contract(WORMHOLE_CONTRACT_ADDR);

    let transfer_info_response = TransferInfoResponse {
        amount: 1000000u32.into(),
        token_address: hex::decode("0000000000000000000000009c3c9283d3e44854697cd22d3faa240cfb032889").unwrap().try_into().unwrap(),
        token_chain: 5,
        recipient: hex::decode("23aae62840414d69ebc26023d1132f59eef316c82222da4644daaa832ea56349").unwrap().try_into().unwrap(),
        recipient_chain: 32,
        fee: 0u32.into(),
        payload: hex::decode("7b2262617369635f726563697069656e74223a7b22726563697069656e74223a22633256704d575636637a56745a4731334f486436646d4e7a4f585a344f586b335a4774306357646c4d336c36626a52334d477735626a5130227d7d").unwrap(),
    };
    let transfer_info_response_copy = transfer_info_response.clone();

    deps.querier.update_wasm(move |q| match q {
        WasmQuery::Smart {
            contract_addr: _,
            msg: _,
        } => SystemResult::Ok(ContractResult::Ok(
            to_binary(&transfer_info_response_copy).unwrap(),
        )),
        _ => SystemResult::Err(SystemError::UnsupportedRequest {
            kind: "wasm".to_string(),
        }),
    });

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let vaa = Binary::from_base64("AAAAAA").unwrap();

    let response = complete_transfer_and_convert(deps.as_mut(), env, info, vaa).unwrap();

    // response should have 1 message
    assert_eq!(response.messages.len(), 1);

    // 1. WasmMsg::Execute (token bridge complete transfer)
    assert_eq!(response.messages[0].id, COMPLETE_TRANSFER_REPLY_ID);
    assert_eq!(response.messages[0].reply_on, ReplyOn::Success);
    assert_eq!(
        response.messages[0].msg,
        CosmosMsg::Wasm(WasmMsg::Execute {
            contract_addr: token_bridge_addr,
            msg: Binary::from_base64("eyJjb21wbGV0ZV90cmFuc2Zlcl93aXRoX3BheWxvYWQiOnsiZGF0YSI6IkFBQUFBQT09IiwicmVsYXllciI6Indvcm1ob2xlMXZoa20ycXY3ODRydWx4OHlscnUwenB2eXZ3M20zY3k5OWU2d3kwIn19").unwrap(),
            funds: vec![]
        })
    );

    // response should have 2 attributes
    assert_eq!(response.attributes.len(), 2);
    assert_eq!(response.attributes[0].key, "action");
    assert_eq!(
        response.attributes[0].value,
        "complete_transfer_with_payload"
    );
    assert_eq!(response.attributes[1].key, "transfer_payload");
    assert_eq!(
        response.attributes[1].value,
        Binary::from(transfer_info_response.clone().payload).to_base64()
    );

    // finally, validate that the state was saved into storage
    let saved_transfer = CURRENT_TRANSFER.load(deps.as_mut().storage).unwrap();
    assert_eq!(saved_transfer, transfer_info_response);
}

// 2. Failure: no token bridge address in state
#[test]
fn complete_transfer_and_convert_no_token_bridge_state() {
    let mut deps = execute_custom_mock_deps();
    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let env = mock_env();
    let vaa = Binary::from_base64("AAAAAA").unwrap();

    let err = complete_transfer_and_convert(deps.as_mut(), env, info, vaa).unwrap_err();
    assert_eq!(
        err.to_string(),
        "could not load token bridge contract address"
    );
}

// 3. Failure: token bridge query TransferInfo failed
#[test]
fn complete_transfer_and_convert_failure_transferinfo_query() {
    let mut deps = execute_custom_mock_deps();
    deps.querier.update_wasm(|q| match q {
        WasmQuery::Smart {
            contract_addr: _,
            msg: _,
        } => SystemResult::Ok(ContractResult::Err("query failed".to_string())),
        _ => SystemResult::Err(SystemError::UnsupportedRequest {
            kind: "wasm".to_string(),
        }),
    });

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let env = mock_env();
    let vaa = Binary::from_base64("AAAAAA").unwrap();

    let err = complete_transfer_and_convert(deps.as_mut(), env, info, vaa).unwrap_err();
    assert_eq!(err.to_string(), "could not parse token bridge payload3 vaa");
}

// 4. Failure: could not humanize recipient address
#[test]
fn complete_transfer_and_convert_failure_humanize_recipient() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();

    let transfer_info_response = to_binary(&cw_token_bridge::msg::TransferInfoResponse {
        amount: 1000000u32.into(),
        token_address: hex::decode("0000000000000000000000009c3c9283d3e44854697cd22d3faa240cfb032889").unwrap().try_into().unwrap(),
        token_chain: 5,
        recipient: hex::decode("6d9ae6b2d333c1d65301a59da3eed388ca5dc60cb12496584b75cbe6b15fdbed").unwrap().try_into().unwrap(),
        recipient_chain: 32,
        fee: 0u32.into(),
        payload: hex::decode("7b2262617369635f726563697069656e74223a7b22726563697069656e74223a22633256704d575636637a56745a4731334f486436646d4e7a4f585a344f586b335a4774306357646c4d336c36626a52334d477735626a5130227d7d").unwrap(),
    }).unwrap();

    deps.querier.update_wasm(move |q| match q {
        WasmQuery::Smart {
            contract_addr: _,
            msg: _,
        } => SystemResult::Ok(ContractResult::Ok(transfer_info_response.clone())),
        _ => SystemResult::Err(SystemError::UnsupportedRequest {
            kind: "wasm".to_string(),
        }),
    });

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let vaa = Binary::from_base64("AAAAAA").unwrap();

    let err = complete_transfer_and_convert(deps.as_mut(), env, info, vaa).unwrap_err();
    assert_eq!(err.to_string(), "Generic error: case not found");
}

// 5. Failure: recipient address doesn't match contract address
#[test]
fn complete_transfer_and_convert_nomatch_recipient_contract() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();

    let transfer_info_response = to_binary(&cw_token_bridge::msg::TransferInfoResponse {
        amount: 1000000u32.into(),
        token_address: hex::decode("0000000000000000000000009c3c9283d3e44854697cd22d3faa240cfb032889").unwrap().try_into().unwrap(),
        token_chain: 5,
        recipient: hex::decode("23aae62840414d69ebc26023d1132f59eef316c82222da4644daaa832ea56349").unwrap().try_into().unwrap(),
        recipient_chain: 32,
        fee: 0u32.into(),
        payload: hex::decode("7b2262617369635f726563697069656e74223a7b22726563697069656e74223a22633256704d575636637a56745a4731334f486436646d4e7a4f585a344f586b335a4774306357646c4d336c36626a52334d477735626a5130227d7d").unwrap(),
    }).unwrap();

    deps.querier.update_wasm(move |q| match q {
        WasmQuery::Smart {
            contract_addr: _,
            msg: _,
        } => SystemResult::Ok(ContractResult::Ok(transfer_info_response.clone())),
        _ => SystemResult::Err(SystemError::UnsupportedRequest {
            kind: "wasm".to_string(),
        }),
    });

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let vaa = Binary::from_base64("AAAAAA").unwrap();

    let err = complete_transfer_and_convert(deps.as_mut(), env, info, vaa).unwrap_err();
    assert_eq!(err.to_string(), "vaa recipient must be this contract");
}

// TESTS: convert_and_transfer
// 1. Happy path
#[test]
fn convert_and_transfer_happy_path() {
    let mut deps = execute_custom_mock_deps();

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();
    let tokenfactory_denom =
        "factory/cosmos2contract/3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETDa".to_string();
    CW_DENOMS
        .save(
            deps.as_mut().storage,
            WORMHOLE_CONTRACT_ADDR.to_string(),
            &tokenfactory_denom,
        )
        .unwrap();
    let coin = coin(1, tokenfactory_denom.clone());

    let info = mock_info(WORMHOLE_USER_ADDR, &[coin.clone()]);
    let env = mock_env();
    let recipient_chain = 2;
    let recipient = Binary::from_base64("AAAAAAAAAAAAAAAAjyagAl3Mxs/Aen04dWKAoQ4pWtc=").unwrap();
    let fee = Uint128::zero();
    let transfer_type = TransferType::Simple { fee };
    let nonce = 0u32;

    let response = convert_and_transfer(
        deps.as_mut(),
        info,
        env,
        recipient,
        recipient_chain,
        transfer_type,
        nonce,
    )
    .unwrap();

    // response should have 3 messages
    assert_eq!(response.messages.len(), 3);

    let mut expected_response: Response<TokenFactoryMsg> = Response::new();
    expected_response = expected_response.add_message(TokenMsg::BurnTokens {
        denom: tokenfactory_denom,
        amount: coin.amount.u128(),
        burn_from_address: "".to_string(),
    });
    expected_response = expected_response.add_message(
        CosmosMsg::Wasm(WasmMsg::Execute {
            contract_addr: WORMHOLE_CONTRACT_ADDR.to_string(),
            msg: Binary::from_base64("eyJpbmNyZWFzZV9hbGxvd2FuY2UiOnsic3BlbmRlciI6ImZha2V0b2tlbmJyaWRnZSIsImFtb3VudCI6IjEiLCJleHBpcmVzIjpudWxsfX0=").unwrap(),
            funds: vec![]
        })
    );
    expected_response = expected_response.add_message(
        CosmosMsg::Wasm(WasmMsg::Execute {
            contract_addr: token_bridge_addr,
            msg: Binary::from_base64("eyJpbml0aWF0ZV90cmFuc2ZlciI6eyJhc3NldCI6eyJpbmZvIjp7InRva2VuIjp7ImNvbnRyYWN0X2FkZHIiOiJ3b3JtaG9sZTF5dzR3djJ6cWc5eGtuNjd6dnEzYXp5ZTB0OGgweDlrZ3lnM2Q1M2p5bTI0Z3h0NDl2ZHlzNnM4aDdhIn19LCJhbW91bnQiOiIxIn0sInJlY2lwaWVudF9jaGFpbiI6MiwicmVjaXBpZW50IjoiQUFBQUFBQUFBQUFBQUFBQWp5YWdBbDNNeHMvQWVuMDRkV0tBb1E0cFd0Yz0iLCJmZWUiOiIwIiwibm9uY2UiOjB9fQ==").unwrap(),
            funds: vec![]
        })
    );

    // 1. TokenMsg::BurnTokens
    assert_eq!(response.messages[0].msg, expected_response.messages[0].msg,);

    // 2. WasmMsg::Execute (increase allowance)
    assert_eq!(response.messages[1].msg, expected_response.messages[1].msg,);

    // 3. WasmMsg::Execute (initiate transfer)
    assert_eq!(response.messages[2].msg, expected_response.messages[2].msg,);
}

// 2. Failure: no token bridge address in state
#[test]
fn convert_and_transfer_no_token_bridge_state() {
    let mut deps = execute_custom_mock_deps();
    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let env = mock_env();
    let recipient_chain = 2;
    let recipient = Binary::from_base64("AAAAAAAAAAAAAAAAjyagAl3Mxs/Aen04dWKAoQ4pWtc=").unwrap();
    let fee = Uint128::zero();
    let transfer_type = TransferType::Simple { fee };
    let nonce = 0u32;

    let err = convert_and_transfer(
        deps.as_mut(),
        info,
        env,
        recipient,
        recipient_chain,
        transfer_type,
        nonce,
    )
    .unwrap_err();
    assert_eq!(
        err.to_string(),
        "could not load token bridge contract address"
    );
}

// 3. Failure: no coin in funds
#[test]
fn convert_and_transfer_no_funds() {
    let mut deps = execute_custom_mock_deps();

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[]);
    let env = mock_env();
    let recipient_chain = 2;
    let recipient = Binary::from_base64("AAAAAAAAAAAAAAAAjyagAl3Mxs/Aen04dWKAoQ4pWtc=").unwrap();
    let fee = Uint128::zero();
    let transfer_type = TransferType::Simple { fee };
    let nonce = 0u32;

    let err = convert_and_transfer(
        deps.as_mut(),
        info,
        env,
        recipient,
        recipient_chain,
        transfer_type,
        nonce,
    )
    .unwrap_err();
    assert_eq!(err.to_string(), "info.funds should contain only 1 coin");
}

// 4. Failure: more coins than expected in funds
#[test]
fn convert_and_transfer_too_many_funds() {
    let mut deps = execute_custom_mock_deps();

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[coin(1, "denomA"), coin(1, "denomB")]);
    let env = mock_env();
    let recipient_chain = 2;
    let recipient = Binary::from_base64("AAAAAAAAAAAAAAAAjyagAl3Mxs/Aen04dWKAoQ4pWtc=").unwrap();
    let fee = Uint128::zero();
    let transfer_type = TransferType::Simple { fee };
    let nonce = 0u32;

    let err = convert_and_transfer(
        deps.as_mut(),
        info,
        env,
        recipient,
        recipient_chain,
        transfer_type,
        nonce,
    )
    .unwrap_err();
    assert_eq!(err.to_string(), "info.funds should contain only 1 coin");
}

// 5. Failure: parse_bank_token_factory_contract method failure
#[test]
fn convert_and_transfer_parse_method_failure() {
    let mut deps = execute_custom_mock_deps();

    let token_bridge_addr = "faketokenbridge".to_string();
    TOKEN_BRIDGE_CONTRACT
        .save(deps.as_mut().storage, &token_bridge_addr)
        .unwrap();

    let info = mock_info(WORMHOLE_USER_ADDR, &[coin(1, "denomA")]);
    let env = mock_env();
    let recipient_chain = 2;
    let recipient = Binary::from_base64("AAAAAAAAAAAAAAAAjyagAl3Mxs/Aen04dWKAoQ4pWtc=").unwrap();
    let fee = Uint128::zero();
    let transfer_type = TransferType::Simple { fee };
    let nonce = 0u32;

    let err = convert_and_transfer(
        deps.as_mut(),
        info,
        env,
        recipient,
        recipient_chain,
        transfer_type,
        nonce,
    )
    .unwrap_err();
    assert_eq!(err.to_string(), "coin is not from the token factory");
}

// TESTS: parse_bank_token_factory_contract
// 1. Happy path
#[test]
fn parse_bank_token_factory_contract_happy_path() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();

    let tokenfactory_denom = format!(
        "factory/{}/{}",
        MOCK_CONTRACT_ADDR, "3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETDa"
    );
    let coin = Coin::new(100, tokenfactory_denom.clone());
    CW_DENOMS
        .save(
            deps.as_mut().storage,
            WORMHOLE_CONTRACT_ADDR.to_string(),
            &tokenfactory_denom,
        )
        .unwrap();

    let contract_addr = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap();
    assert_eq!(contract_addr, WORMHOLE_CONTRACT_ADDR);
}

// 2. Failure: parsed denom not of length 3
#[test]
fn parse_bank_token_factory_contract_failure_denom_length() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(100, "tokenfactory/denom");

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(method_err.to_string(), "coin is not from the token factory");
}

// 3. Failure: parsed denom[0] != "factory"
#[test]
fn parse_bank_token_factory_contract_failure_non_factory_token() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(100, "tokenfactory/contract/denom");

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(method_err.to_string(), "coin is not from the token factory");
}

// 4. Failure: parsed denom[1] != contract address
#[test]
fn parse_bank_token_factory_contract_failure_non_contract_created() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(100, "factory/contract/denom");

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(method_err.to_string(), "coin is not from the token factory");
}

// 5. Failure: contract_addr_from_base58 method failure
#[test]
fn parse_bank_token_factory_contract_failure_base58_decode_failure() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(100, format!("factory/{MOCK_CONTRACT_ADDR}/denom0"));

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(
        method_err.to_string(),
        "failed to decode base58 subdenom denom0"
    );
}

// 6. Failure: the parsed contract address is not in CW_DENOMS storage
#[test]
fn parse_bank_token_factory_contract_failure_no_storage() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(
        100,
        format!(
            "factory/{}/{}",
            MOCK_CONTRACT_ADDR, "3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETDa"
        ),
    );

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(
        method_err.to_string(),
        "a corresponding denom for the extracted contract addr is not contained in storage"
    );
}

// 7. Failure: the stored denom doesn't equal the coin's denom
#[test]
fn parse_bank_token_factory_contract_failure_storage_mismatch() {
    let mut deps = execute_custom_mock_deps();
    let env = mock_env();
    let coin = Coin::new(
        100,
        format!(
            "factory/{}/{}",
            MOCK_CONTRACT_ADDR, "3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETDa"
        ),
    );

    CW_DENOMS
        .save(
            deps.as_mut().storage,
            WORMHOLE_CONTRACT_ADDR.to_string(),
            &"factory/fake/fake".to_string(),
        )
        .unwrap();

    let method_err = parse_bank_token_factory_contract(deps.as_mut(), env, coin).unwrap_err();
    assert_eq!(
        method_err.to_string(),
        "the stored denom for the contract does not match the actual coin denom"
    );
}

// TESTS: contract_addr_from_base58
// 1. Happy path: convert to contract address
#[test]
fn contract_addr_from_base58_happy_path() {
    let deps = execute_custom_mock_deps();
    let contract_addr = contract_addr_from_base58(
        deps.as_ref(),
        "3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETDa",
    )
    .unwrap();
    assert_eq!(
        contract_addr,
        "wormhole1yw4wv2zqg9xkn67zvq3azye0t8h0x9kgyg3d53jym24gxt49vdys6s8h7a"
    );
}

// 2. Failure: could not decode base58
#[test]
fn contract_addr_from_base58_failure_decode_base58() {
    let deps = execute_custom_mock_deps();
    let method_err = contract_addr_from_base58(
        deps.as_ref(),
        "3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETD0",
    )
    .unwrap_err();
    assert_eq!(
        method_err.to_string(),
        "failed to decode base58 subdenom 3QEQyi7iyJHwQ4wfUMLFPB4kRzczMAXCitWh7h6TETD0"
    )
}

// 1. happy path
#[test]
fn submit_update_chain_to_channel_map_happy_path() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAs=").unwrap();

    let response = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap();

    // response should have 0 message
    assert_eq!(response.messages.len(), 0);
    assert_eq!(
        response,
        Response::new().add_event(
            Event::new("UpdateChainToChannelMap")
                .add_attribute("chain_id", "Karura".to_string())
                .add_attribute("channel_id", "channel-1".to_string()),
        )
    );
}

// 2. failed to parse vaa
#[test]
fn submit_update_chain_to_channel_map_failure_parse_vaa() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AAAABQCPOJcHa/xtVL9+sHyaS7ZU7HxXsQXEo1hjh17e9YeNyCTZQ0YfhTF+HiKTHcgZWPNixGTxmQ0/1XrUrqRYEeM2AQEy0QKlDBn6ga2gQDHSNdm0ZN2ldmrsJbPo7gTrlHvpFzoWK1CEIpba+CRgVUPlVtzLe8pDOZzlKv/sVkQXcxLjAAJuOPAeDOu8byFDsOAWmFM+aucKJ9IVy0fMNI6HDt/W13cZLEfmSk3r3xB4JZh9LYtJRXbjBs56rtwCfJ/Vm8rXAQNyQgtCXeTx3rvHALgJ2E7AXXg54NZx8zKlLFPa/9gQrBqLhI66oGInMeUrcLgao8iE+3nXtv98Iyn6MTizDHkFAATUdU1kIWPCxCQ4KnrV7M6bCoYfcXiad7Ez0jaXysSmvCxy/HuDHKBxMG2d7gTLKjQ1Va4tb0h8RZKzmiyGCMKrAQAAAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAOIAAAAAAAAAAAAAAAAAAAAAAAAABJYmNUcmFuc2xhdG9yAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNoYW5uZWwtMQAL").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "failed to parse VAA header")
}

// 3. unsupported VAA version
#[test]
fn submit_update_chain_to_channel_map_failure_unsupported_vaa_version() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AAAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAs=").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "unsupported VAA version")
}

// 4. not a governance vaa
#[test]
fn submit_update_chain_to_channel_map_failure_not_gov_vaa() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAs=").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "not a governance VAA")
}

// 5. failed to parse governance packet
#[test]
fn submit_update_chain_to_channel_map_failed_parsing_gov_packet() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAsR").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "failed to parse governance packet")
}

// 6. governance vaa is for another chain
#[test]
fn submit_update_chain_to_channel_map_failure_gov_vaa_for_another_chain() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAs=").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "this governance VAA is for another chain")
}

// 7. governance vaa already executed
#[test]
fn submit_update_chain_to_channel_map_failure_gov_vaa_already_executed() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xAAs=").unwrap();

    submit_update_chain_to_channel_map(deps.as_mut(), vaa.clone()).unwrap();
    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "governance vaa already executed")
}

// 8. chain is for wormchain
#[test]
fn submit_update_chain_to_channel_map_failure_chain_id_is_wormchain() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lbC0xDCA=").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(
        err.to_string(),
        "the ibc-translator contract should not maintain channel mappings to wormchain"
    )
}
// 9. failed to parse channel-id
#[test]
fn submit_update_chain_to_channel_map_invalid_channel_id() {
    let mut deps = execute_custom_mock_deps();
    let vaa = Binary::from_base64("AQAAAAAFAI84lwdr/G1Uv36wfJpLtlTsfFexBcSjWGOHXt71h43IJNlDRh+FMX4eIpMdyBlY82LEZPGZDT/VetSupFgR4zYBATLRAqUMGfqBraBAMdI12bRk3aV2auwls+juBOuUe+kXOhYrUIQiltr4JGBVQ+VW3Mt7ykM5nOUq/+xWRBdzEuMAAm448B4M67xvIUOw4BaYUz5q5won0hXLR8w0jocO39bXdxksR+ZKTevfEHglmH0ti0lFduMGznqu3AJ8n9WbytcBA3JCC0Jd5PHeu8cAuAnYTsBdeDng1nHzMqUsU9r/2BCsGouEjrqgYicx5StwuBqjyIT7ede2/3wjKfoxOLMMeQUABNR1TWQhY8LEJDgqetXszpsKhh9xeJp3sTPSNpfKxKa8LHL8e4McoHEwbZ3uBMsqNDVVri1vSHxFkrOaLIYIwqsBAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAAAAAEliY1RyYW5zbGF0b3IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2hhbm5lAC3/AAs=").unwrap();

    let err = submit_update_chain_to_channel_map(deps.as_mut(), vaa).unwrap_err();

    assert_eq!(err.to_string(), "failed to parse channel-id as utf-8")
}
