import os

from django.conf import settings

from sandbox.apps.firmware.core import tl_service_api
from sandbox.apps.provision.core.common import get_tp_process, increment_version, provision_generated_predicate
from sandbox.apps.provision.core.tp_wrapper import TPWrapper
from sandbox.libs.context.models import SandboxActions
from sandbox.libs.context.models import SandboxContext
from sandbox.libs.decorators import threaded_action
from sandbox.libs.utils import find_files, cleanup_folder
from sandbox.libs.logs.core.ws import ws_log_message


@threaded_action(action=SandboxActions.GENERATE_PROVISION,
                 forbidden_on=[SandboxActions.GENERATE_PROVISION],
                 predicate=(lambda: not SandboxContext.is_provision_generated(),
                            'provision package is already generated'))
def generate_provision_pack():
    process = get_tp_process()
    _register_callbacks(process)
    process.run_in_thread()
    try:
        w = TPWrapper(process)
        w.generate_upper_level_keys()
        w.generate_release_trust_list()
        w.export_provision_package()
        w.exit()
    except Exception as e:
        # Something went wrong - remove all generated files
        # to avoid errors on next launches
        cleanup_folder(settings.SANDBOX_TP_DIR)
        raise e
    finally:
        process.kill()
    # Update Sandbox context
    SandboxContext.set_provision_generated(True)


@threaded_action(action=SandboxActions.PUBLISH_NEW_TRUST_LIST,
                 forbidden_on=[SandboxActions.GENERATE_PROVISION,
                               SandboxActions.PUBLISH_NEW_TRUST_LIST],
                 predicate=provision_generated_predicate)
def publish_new_tl():
    tl_path, version = _generate_incremented_tl_version()
    upload_response = tl_service_api.upload_file(tl_path)
    if upload_response.status_code != 201:
        raise Exception(f'Failed to upload TrustList to cloud\n'
                        f'Status code: {upload_response.status_code}\n'
                        f'Text: {upload_response.text}')
    ws_log_message(f'TrustList v{version} successfully uploaded to cloud')

    tl_id = upload_response.json().get('id')
    publish_response = tl_service_api.publish(100, [], tl_id)
    if publish_response.status_code != 200:
        raise Exception(f'Failed to publish TrustList\n'
                        f'Status code: {publish_response.status_code}\n'
                        f'Text: {publish_response.text}')
    ws_log_message(f'TrustList v{version} successfully published')


def _generate_incremented_tl_version():
    # Determine latest TL version at cloud
    latest = tl_service_api.get_latest_version()
    if latest is None:
        version = '0.0.1.1'
    else:
        version = increment_version(latest)
    process = get_tp_process()
    _register_callbacks(process)
    process.run_in_thread()

    try:
        w = TPWrapper(process)
        w.generate_release_trust_list(version=version)
        w.exit()
    finally:
        process.kill()

    # Return path to generated TL
    trust_lists = find_files(path=settings.SANDBOX_TP_DIR,
                             pattern='TrustList_')
    *_, tl_path = sorted(trust_lists, key=lambda path: os.path.getctime(path))
    return tl_path, version


def _register_callbacks(tp_process):
    def _get_cb(msg):
        return lambda: ws_log_message(msg)

    for msg, event in [
        ('Generating Recovery key...', 'Generating Recovery Key'),
        ('Generating Auth Key...', 'Generating Auth Key'),
        ('Generating TrustList Service Key...', 'Generating TrustList Service Key'),
        ('Generating Firmware Key...', 'Generating Firmware Key'),
        ('Generating Factory Key...', 'Generating Factory Key'),
        ('Generating TrustList...', 'Generating TrustList...'),
        ('TrustList generated and stored...', 'TrustList generated and stored'),
        ('Provision package saved...', 'Provision package for Factory saved as'),
    ]:
        tp_process.register_callback_on_event(_get_cb(msg), event)
