#!/usr/bin/env python3
"""
This script builds the default set of network names for each MESC implementation

Name data is sourced from https://chainid.network

This script creates the following files in the MESC repository:
- ./python/mesc/network_names.py
- ./rust/crates/mesc/src/network_names.rs
"""
from __future__ import annotations

import os
import requests
from typing import Mapping, MutableMapping


# specialcase the standard name for certain chains
special_cases: Mapping[str, str] = {
    'OP Mainnet': 'optimism',
    'Genesis Coin': 'genesis_coin',
    'X1 Network': 'x1_network',
    'ThaiChain 2.0 ThaiFi': 'thaifi',
    'WEMIX3.0 Mainnet': 'wemix',
    'WEMIX3.0 Testnet': 'wemix_testnet',
}


def get_network_names() -> Mapping[str, str]:
    # fetch raw network data
    url = 'https://chainid.network/chains.json'
    result = requests.get(url)
    data = result.json()

    network_names: MutableMapping[str, str] = {}
    for datum in data:
        # standardize name
        filtered = standardize_name(datum['name'])

        # skip deprecated networks
        if 'deprecated' in filtered:
            continue

        if filtered in network_names.values():
            continue
        network_names[str(datum['chainId'])] = filtered

    return network_names


def standardize_name(name: str) -> str:
    """put name into standard format"""

    # special cases
    if name in special_cases:
        return special_cases[name]

    # replace special characters
    name = name.lower()
    name = name.replace(' ', '_')
    name = name.replace('-', '_')
    name = name.replace('(', '')
    name = name.replace(')', '')

    # remove keywords
    while True:
        remove = [
            '_mainnet',
            '_network',
            '_smart_chain',
            '_l1',
            '_sidechain',
            'sidechain',
            '_chain',
            'chain',
            '_coin',
        ]
        for piece in remove:
            if piece in name:
                name = name.replace(piece, '')
                break
        else:
            break

    # strip stray
    name = name.strip()
    name = name.strip('_')
    name = name.strip('-')

    return name


# common data
preamble = """Default mapping between chain_id's and network names

This file is generated by `python/generate_network_names.py` in the MESC repo.

The same set of network names is used for each MESC implementation.

Do not edit this file manually."""

# python data
python_path = './mesc/network_names.py'
python_template = '''"""{preamble}"""

network_names = {name_data}
'''


def generate_python_content(network_names: Mapping[str, str]) -> str:
    name_data = '{'
    for chain_id, network_name in network_names.items():
        name_data += "\n    '" + chain_id + "': '" + network_name + "',"
    name_data += '\n}'
    return python_template.format(preamble=preamble, name_data=name_data)


# rust data
rust_path = '../rust/crates/mesc/src/network_names.rs'
rust_template = """{preamble}

use crate::{{ChainId, TryIntoChainId}};
use std::collections::HashMap;

/// get default mapping between chain_id's and network names
pub fn get_network_names() -> HashMap<ChainId, String> {{
    {name_data}
}}
"""


def generate_rust_content(network_names: Mapping[str, str]) -> str:
    rust_preamble = '\n'.join(('//! ' + line).strip() for line in preamble.split('\n'))
    name_data = '['
    for chain_id, network_name in network_names.items():
        name_data += '\n        ("' + chain_id + '", "' + network_name + '"),'
    name_data += """\n    ]
    .into_iter()
    .filter_map(|(chain_id, name)| match chain_id.try_into_chain_id() {
        Ok(chain_id) => Some((chain_id, name.to_string())),
        Err(_) => None,
    })
    .collect()"""
    return rust_template.format(preamble=rust_preamble, name_data=name_data)


if __name__ == '__main__':
    network_names = get_network_names()

    # change working directory to parent of this script
    os.chdir(os.path.dirname(os.path.realpath(__file__)))

    # create python file
    python_content = generate_python_content(network_names)
    with open(python_path, 'w') as f:
        f.write(python_content)
    print('wrote', python_path)

    # create rust file
    rust_content = generate_rust_content(network_names)
    with open(rust_path, 'w') as f:
        f.write(rust_content)
    print('wrote', rust_path)
