#  Copyright (C) 2024
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
import logging
from typing import TypeVar, Iterable

from .parse_args_as_dataclass import parse_args_as_file_with_kwargs
from .repr_arg_name_to_exception import repr_arg_name_to_exception
from .read_file_as_dict import read_file_as_dict
from .. import make_ini_from_hint
from .._hint import get_hint_from_dataclass
from .._cast import unflatten_dict
from .._dataclass import FieldErrors, to_dataclass
from .._utils import update_dict_recursive
from .._type import DataclassProtocol

logger = logging.getLogger(__file__)
DP = TypeVar('DP', bound=DataclassProtocol)
DEFAULT_KEY_DEPTH_SEPARATOR = '.'


def parse_args_as_dataclass_or_exit(
        dataclass_type: type[DP],
        args: Iterable[str] | None = None,  # None means sys.argv[1:]
        flag_name: str | None = None,  # None or empty str means default
        key_value_separator: str | None = None,  # None or empty str means default
        key_depth_separator: str | None = None,  # None or empty str means default
        exit_code: int = 2,
) -> DP:
    key_depth_separator = key_depth_separator or DEFAULT_KEY_DEPTH_SEPARATOR
    filepath, flat_overload = parse_args_as_file_with_kwargs(
        args=args, kwarg_flag_name=flag_name, key_value_separator=key_value_separator)

    if filepath:
        try:
            config_dict = read_file_as_dict(filepath)
        except Exception as e:
            logger.critical(f'Failed to read config file({filepath!r}): {e!r}')
            exit(exit_code)
        logger.warning(f'Red config from file: {filepath!r}')
    else:
        config_dict = {}
        logger.warning(f'No config file passed, init empty config')

    if flat_overload:
        update_dict_recursive(config_dict, unflatten_dict(flat_overload, separator=key_depth_separator))
        logger.warning(f'Updated config with {len(flat_overload)} arguments from cmd arguments')
    else:
        logger.warning(f'No config updates from cmd arguments passed')

    try:
        # todo: raise is extra fields are passed
        return to_dataclass(config_dict, dataclass_type)
    except FieldErrors as e:
        for line in repr_arg_name_to_exception(e.field_to_error):
            logger.critical(line)

        if not filepath and not flat_overload:
            example_ini = make_ini_from_hint(get_hint_from_dataclass(dataclass_type), add_optional=False)
            logger.warning('')
            logger.warning("MINIMAL config.ini EXAMPLE:")
            for line in example_ini.splitlines():
                logger.warning(line)
        exit(exit_code)
