# copyright: (c) 2021 by Jesse Johnson.
# license: MPL-2.0, see LICENSE for more details.
"""Provide project initialization."""

import logging
import os
import shutil
from dataclasses import asdict
from typing import TYPE_CHECKING, Any, Dict

import keyring
import pyinputplus as pyip
from invoke import Executor, task

from proman_workflows import WorkflowProgram, __title__, __version__, config
from proman_workflows.collection import Collection
from proman_workflows.config import DocsConfig, ProjectDirs, WorkflowConfig

# TODO: switch to executor
from proman_workflows.pki.gpg import ELYPTICAL_KEY_TYPES, KEY_TYPES

if TYPE_CHECKING:
    from gpg import GenKey
    from invoke import Context

logging.getLogger(__name__).addHandler(logging.NullHandler())


@task
def _setup_executor(ctx):  # type: (Context) -> None
    """Set context."""
    configuration = {
        'plugins': [
            {
                'name': 'vcs',
                'driver_name': 'git',
                'driver_namespace': 'proman.workflows.vcs',
            },
            {
                'name': 'gpg',
                'driver_name': 'gpg',
                'driver_namespace': 'proman.workflows.pki',
            },
        ],
        **asdict(ProjectDirs()),
    }
    length = len(configuration['plugins']) - 1
    if 'plugins' in ctx:
        for i, plugin in enumerate(configuration['plugins']):
            for update in ctx['plugins']:
                if update['name'] == plugin['name']:
                    configuration['plugins'][i].update(plugin)
                elif i == length and update not in configuration['plugins']:
                    configuration['plugins'].append(update)

    setup_tasks = Collection(configuration=configuration)
    ctx.__executor = Executor(setup_tasks)


def create_signingkey(ctx, name, email):  # type: (Context, str, str) -> GenKey
    """Create a signing key."""
    # TODO: get comment
    settings = {
        'name': name,
        'email': email,
        'comment': 'generated by proman_workflows',
        'key_type': 'EDDSA',
        'subkey_type': 'EDDSA',
        'expire_date': '1y',
    }

    rsp = pyip.inputYesNo('Perform adanced GPG setup (y/n): ')
    if rsp == 'yes':
        # TODO: enum auto
        settings['key_type'] = pyip.inputMenu(
            KEY_TYPES,
            prompt='Select GPG key type:\n',
            default=settings['key_type'],
            numbered=True,
        )
        if settings['key_type'] not in ELYPTICAL_KEY_TYPES:
            settings['subkey_length'] = pyip.inputMenu(
                [1024, 2048, 3076, 4096],
                prompt='Select key size:\n',
                default=4096,
            )
        # elif key_type in ELYPTICAL_KEY_TYPES:
        #     if key_curve:
        #         # and key_curve in KEY_CURVES:
        #         settings['key_curve'] = key_curve
        # if key_usage and all(item in key_usage for item in usage):
        #     settings['key_usage'] = ','.join(key_usage)

        settings['subkey_type'] = pyip.inputMenu(
            KEY_TYPES,
            prompt='Select GPG subkey type:\n',
            default=settings['subkey_type'],
            numbered=True,
        )

        # key_length = pyip.inputInt('Enter key length: ')
        settings['expire_date'] = pyip.inputStr(
            prompt='Enter expiration date: ',
            allowRegexes=[r'^(0|([\d]{1,4}[d|w|m|y]))$'],
            default=settings['expire_date'],
        )
    settings['password'] = pyip.inputPassword(
        'Enter GPG password: ', limit=255
    )

    key = ctx.__executor.execute(('gpg.gen_key', settings))

    signingkey = key['keyid']
    if not keyring.get_password(f"{name}-signingkey", signingkey):
        keyring.set_password(
            f"{name}-signingkey", signingkey, settings['password']
        )

    return signingkey


def setup_gitconfig(ctx, update):  # type: (Context, bool) -> Dict[str, Any]
    """Ensure version control system is setup."""
    print('Check git user info is setup.', end='\n\n')
    result = ctx.__executor.execute(('vcs.config.load', {'scope': 'global'}))
    gitconfig = [y for x, y in result.items() if x.name == 'load'][0]

    # select gpg if found else generate gpg key
    if 'user' not in gitconfig['sections']:
        print('Verfying git user is defined...', end=' ')

    # if not config.retrieve('.gpg.signingkey'):
    try:
        print('Verfying git user.name is defined...', end=' ')
        name = gitconfig['sections']['user']['name']
        print('found')
    except Exception as err:
        logging.info(err)
        print('missing', end='\n\n')
        name = pyip.inputStr('Enter git user.name: ', limit=255)
        gitconfig['sections']['user']['name'] = name

    try:
        print('Verfying git user.email is defined...', end=' ')
        email = gitconfig['sections']['user']['email']
        print('found')
    except Exception as err:
        logging.info(err)
        print('missing', end='\n\n')
        email = pyip.inputEmail('Enter git user.email: ', limit=255)
        gitconfig['sections']['user']['email'] = email

    try:
        print('Verfying git user.sigingkey is defined...', end=' ')
        signingkey = gitconfig['sections']['user']['signingkey']
        print('found')
    except Exception as err:
        logging.info(f"{err}: no gpg signingkey defined")
        print('missing', end='\n\n')

        result = ctx.__executor.execute(
            ('gpg.list_keys', {'secret': False, 'keys': None, 'sigs': False})
        )
        gpg_keys = [y for x, y in result.items() if x.name == 'list_keys'][0]

        choices = ['Create a new subkey', 'Skip']
        if gpg_keys != []:
            print(
                'A GPG key was found but user.signingkey is undefined:',
                end='\n\n',
            )
            choices = [
                f"{k['keyid']}:{k['uids'][0]}" for k in gpg_keys
            ] + choices

        selection = pyip.inputMenu(
            choices=choices,
            prompt='Create a key or use an existing one:\n',
            numbered=True,
        )

        signingkey = None
        if selection == 'Create a new subkey':
            signingkey = create_signingkey(ctx, name, email)
        elif selection == 'Skip':
            return gitconfig
        else:
            signingkey = selection.split(':')[0]

        if signingkey:
            gitconfig['sections']['user']['signingkey'] = signingkey

    # setup keyring secrets
    if not hasattr(ctx, 'gpg'):
        ctx.gpg = dict()

    # TODO: setup gpg commit signing
    # TODO: setup gpg package signing

    ctx.__executor.execute(
        (
            'vcs.config.dump',
            {
                'data': gitconfig,
                'template_name': 'gitconfig',
                'dest': os.path.join(os.path.expanduser('~'), '.gitconfig'),
                'update': update,
            },
        )
    )
    return gitconfig


def setup_gitignore(ctx):  # type: (Context) -> None
    """Check gitignore setup."""
    ...


def setup_githooks(ctx):  # type: (Context) -> None
    """Check githooks setup."""
    ...


@task(_setup_executor)
def setup(ctx, update=False):  # type: (Context, bool) -> None
    """Configure workspace for project development."""
    clear = shutil.which('clear') or shutil.which('cls')
    if clear:
        ctx.run(clear)
    print('This tool will assist with environment setup.', end='\n\n')

    # from pprint import pprint
    # pprint(ctx.config.tool.proman.workflows)
    # pprint(ctx.config.__dict__)
    # pprint(ctx.yey)

    setup_gitconfig(ctx, update)
    # setup_gitignore(ctx)
    # setup_githooks(ctx)

    # if not os.path.exists(config.directory):
    #     os.mkdir(ctx.config_dir)

    # if update or not os.path.exists(config.filepath):
    #     config.dump()


project_config = config.ProjectConfig(
    docs=asdict(DocsConfig()),
    plugins=[
        config.Plugin(
            name='vcs',
            driver_name='git',
            driver_namespace='proman.workflows.vcs',
        ),
        config.Plugin(
            name='sort-headers',
            driver_name='isort',
            driver_namespace='proman.workflows.formatter',
        ),
        config.Plugin(
            name='format',
            driver_name='black',
            driver_namespace='proman.workflows.formatter',
        ),
        config.Plugin(
            name='gpg',
            driver_name='gpg',
            driver_namespace='proman.workflows.pki',
        ),
        config.Plugin(
            name='tls',
            driver_name='tls',
            driver_namespace='proman.workflows.pki',
        ),
    ],
)
namespace = Collection(setup)
namespace.configure(asdict(project_config))
namespace.load_collections()

setup = WorkflowProgram(
    name=__title__,
    namespace=namespace,
    binary='workflow-setup',
    binary_names=['workflow-setup'],
    config_class=WorkflowConfig,
    version=__version__,
)

__all__ = ['namespace', 'setup']
