#!/usr/bin/env python3

__package__ = 'archivebox.cli'

import sys
import rich_click as click
from rich import print
from benedict import benedict

from archivebox.misc.util import docstring, enforce_types
from archivebox.misc.toml_util import CustomTOMLEncoder


@enforce_types
def config(*keys,
          get: bool=False,
          set: bool=False, 
          search: bool=False,
          reset: bool=False,
          **kwargs) -> None:
    """Get and set your ArchiveBox project configuration values"""

    from archivebox.misc.checks import check_data_folder
    from archivebox.misc.logging_util import printable_config
    from archivebox.config.collection import load_all_config, write_config_file, get_real_name
    from archivebox.config.configset import get_flat_config, get_all_configs

    check_data_folder()

    FLAT_CONFIG = get_flat_config()
    CONFIGS = get_all_configs()
    
    config_options: list[str] = list(kwargs.pop('key=value', []) or keys or [f'{key}={val}' for key, val in kwargs.items()])
    no_args = not (get or set or reset or config_options)

    matching_config = {}
    if search:
        if config_options:
            config_options = [get_real_name(key) for key in config_options]
            matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG}
            for config_section in CONFIGS.values():
                aliases = getattr(config_section, 'aliases', {})
                
                for search_key in config_options:
                    # search all aliases in the section
                    for alias_key, key in aliases.items():
                        if search_key.lower() in alias_key.lower():
                            matching_config[key] = dict(config_section)[key]
                    
                    # search all keys and values in the section
                    for existing_key, value in dict(config_section).items():
                        if search_key.lower() in existing_key.lower() or search_key.lower() in str(value).lower():
                            matching_config[existing_key] = value
            
        print(printable_config(matching_config))
        raise SystemExit(not matching_config)

    elif get or no_args:
        if config_options:
            config_options = [get_real_name(key) for key in config_options]
            matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG}
            failed_config = [key for key in config_options if key not in FLAT_CONFIG]
            if failed_config:
                print('\n[red][X] These options failed to get[/red]')
                print('    {}'.format('\n    '.join(config_options)))
                raise SystemExit(1)
        else:
            matching_config = FLAT_CONFIG

        # Display core config sections
        for config_section in CONFIGS.values():
            if hasattr(config_section, 'toml_section_header'):
                print(f'[grey53]\\[{config_section.toml_section_header}][/grey53]')
            else:
                print('[grey53]\\[CONSTANTS]                                        # (read-only)[/grey53]')

            kv_in_section = {key: val for key, val in dict(config_section).items() if key in matching_config}
            print(benedict(kv_in_section).to_toml(encoder=CustomTOMLEncoder()).strip().replace('\n\n', '\n'))
            print('[grey53]################################################################[/grey53]')

        # Display plugin config section
        from archivebox.hooks import discover_plugin_configs

        plugin_configs = discover_plugin_configs()
        plugin_keys = {}

        # Collect all plugin config keys
        for plugin_name, schema in plugin_configs.items():
            if 'properties' not in schema:
                continue
            for key in schema['properties'].keys():
                if key in matching_config:
                    plugin_keys[key] = matching_config[key]

        # Display all plugin config in single [PLUGINS] section
        if plugin_keys:
            print(f'[grey53]\\[PLUGINS][/grey53]')
            print(benedict(plugin_keys).to_toml(encoder=CustomTOMLEncoder()).strip().replace('\n\n', '\n'))
            print('[grey53]################################################################[/grey53]')

        raise SystemExit(not matching_config)

    elif set:
        new_config = {}
        failed_options = []
        for line in config_options:
            if line.startswith('#') or not line.strip():
                continue
            if '=' not in line:
                print('[red][X] Config KEY=VALUE must have an = sign in it[/red]')
                print(f'    {line}')
                raise SystemExit(2)

            raw_key, val = line.split('=', 1)
            raw_key = raw_key.upper().strip()
            key = get_real_name(raw_key)
            if key != raw_key:
                print(f'[yellow][i] Note: The config option {raw_key} has been renamed to {key}, please use the new name going forwards.[/yellow]')

            if key in FLAT_CONFIG:
                new_config[key] = val.strip()
            else:
                failed_options.append(line)

        if new_config:
            before = FLAT_CONFIG
            matching_config = write_config_file(new_config)
            after = {**load_all_config(), **get_flat_config()}
            print(printable_config(matching_config))

            side_effect_changes = {}
            for key, val in after.items():
                if key in FLAT_CONFIG and (str(before[key]) != str(after[key])) and (key not in matching_config):
                    side_effect_changes[key] = after[key]

            if side_effect_changes:
                print(file=sys.stderr)
                print('[yellow][i] Note: This change also affected these other options that depended on it:[/yellow]', file=sys.stderr)
                print('    {}'.format(printable_config(side_effect_changes, prefix='    ')), file=sys.stderr)

        if failed_options:
            print()
            print('[red][X] These options failed to set (check for typos):[/red]')
            print('    {}'.format('\n    '.join(failed_options)))
            raise SystemExit(1)

    elif reset:
        print('[red][X] This command is not implemented yet.[/red]')
        print('    Please manually remove the relevant lines from your config file:')
        raise SystemExit(2)

    else:
        print('[red][X] You must pass either --get or --set, or no arguments to get the whole config.[/red]')
        print('    archivebox config')
        print('    archivebox config --get SOME_KEY')
        print('    archivebox config --set SOME_KEY=SOME_VALUE')
        raise SystemExit(2)


@click.command()
@click.option('--search', is_flag=True, help='Search config KEYs, VALUEs, and ALIASES for the given term')
@click.option('--get', is_flag=True, help='Get the value for the given config KEYs')
@click.option('--set', is_flag=True, help='Set the given KEY=VALUE config values')
@click.option('--reset', is_flag=True, help='Reset the given KEY config values to their defaults')
@click.argument('KEY=VALUE', nargs=-1, type=str)
@docstring(config.__doc__)
def main(**kwargs) -> None:
    config(**kwargs)


if __name__ == '__main__':
    main()
