import os
import logging

from io import StringIO
from collections import OrderedDict


_logger = logging.getLogger(__name__)
_products_dir = os.path.join(
    os.path.dirname(__file__),
    '..', '..', '..',
    'platformio'
)


def get_products():
    products = []
    for f in os.listdir(_products_dir):
        # temp hack
        if f.endswith('-esp01'):
            continue
        # skip the common dir
        if f in ('common',):
            continue

        if os.path.isdir(os.path.join(_products_dir, f)):
            products.append(f)

    return products


def platformio_ini(product_config: dict,
                   target: str,
                   # node_id: str,
                   build_specific_defines: dict,
                   build_specific_defines_enums: list[str],
                   platform: str,
                   framework: str = 'arduino',
                   upload_port: str = '/dev/ttyUSB0',
                   monitor_speed: int = 115200,
                   debug=False,
                   debug_network=False) -> str:
    node_id = build_specific_defines['CONFIG_NODE_ID']

    # defines
    defines = {
        **product_config['common_defines'],
        'CONFIG_NODE_ID': node_id,
        'CONFIG_WIFI_AP_SSID': ('HK_'+node_id)[:31]
    }
    try:
        defines.update(product_config['target_defines'][target])
    except KeyError:
        pass
    defines['CONFIG_NODE_SECRET_SIZE'] = len(defines['CONFIG_NODE_SECRET'])
    defines['CONFIG_MQTT_CLIENT_ID'] = node_id

    build_type = 'release'
    if debug:
        defines['DEBUG'] = True
        build_type = 'debug'
    if debug_network:
        defines['DEBUG'] = True
        defines['DEBUG_ESP_SSL'] = True
        defines['DEBUG_ESP_PORT'] = 'Serial'
        build_type = 'debug'
    if build_specific_defines:
        for k, v in build_specific_defines.items():
            defines[k] = v
    defines = OrderedDict(sorted(defines.items(), key=lambda t: t[0]))

    # libs
    libs = []
    if 'common_libs' in product_config:
        libs.extend(product_config['common_libs'])
    if 'target_libs' in product_config and target in product_config['target_libs']:
        libs.extend(product_config['target_libs'][target])
    libs = list(set(libs))
    libs.sort()

    try:
        target_real_name = product_config['target_board_names'][target]
    except KeyError:
        target_real_name = target

    buf = StringIO()

    buf.write('; Generated by pio_ini.py\n\n')
    buf.write(f'[env:{target_real_name}]\n')
    buf.write(f'platform = {platform}\n')
    buf.write(f'board = {target_real_name}\n')
    buf.write(f'framework = {framework}\n')
    buf.write(f'upload_port = {upload_port}\n')
    buf.write(f'monitor_speed = {monitor_speed}\n')
    if libs:
        buf.write(f'lib_deps =')
        for lib in libs:
            buf.write(f'    {lib}\n')
    buf.write(f'build_flags =\n')
    if defines:
        for name, value in defines.items():
            buf.write(f'    -D{name}')
            is_enum = name in build_specific_defines_enums
            if type(value) is not bool:
                buf.write('=')
                if type(value) is str:
                    if not is_enum:
                        buf.write('"\\"')
                    value = value.replace('"', '\\"')
                buf.write(f'{value}')
                if type(value) is str and not is_enum:
                    buf.write('"\\"')
            buf.write('\n')
        buf.write(f'    -I../common/include')
    buf.write(f'\nbuild_type = {build_type}')

    return buf.getvalue()
