import os
import shutil

from django.conf import settings

from sandbox.apps.devices.core.consts import EmulatorModels, EmulatorManufacturers
from sandbox.apps.firmware.core import fw_service_api
from sandbox.apps.firmware.core.signer import FWSigner
from sandbox.apps.provision.core.common import increment_version, provision_generated_predicate
from sandbox.libs.context.models import SandboxActions
from sandbox.libs.decorators import threaded_action
from sandbox.libs.logs.core.ws import ws_log_message


@threaded_action(action=SandboxActions.PUBLISH_NEW_GATEWAY_EMULATOR_FW,
                 forbidden_on=[SandboxActions.GENERATE_PROVISION,
                               SandboxActions.PUBLISH_NEW_GATEWAY_EMULATOR_FW],
                 predicate=provision_generated_predicate)
def publish_new_gateway_emulator_fw():
    src = shutil.which('gateway')
    model = EmulatorModels.GATEWAY
    manufacturer = EmulatorManufacturers.GATEWAY
    output_files = _generate_incremented_fw_version(src, model, manufacturer)
    _publish(output_files.update_file)


@threaded_action(action=SandboxActions.PUBLISH_NEW_DEVICE_EMULATOR_FW,
                 forbidden_on=[SandboxActions.GENERATE_PROVISION,
                               SandboxActions.PUBLISH_NEW_DEVICE_EMULATOR_FW],
                 predicate=provision_generated_predicate)
def publish_new_device_emulator_fw():
    src = shutil.which('thing')
    model = EmulatorModels.IOT_DEVICE
    manufacturer = EmulatorManufacturers.IOT_DEVICE
    output_files = _generate_incremented_fw_version(src, model, manufacturer)
    _publish(output_files.update_file)


def _publish(update_file):
    # Upload
    upload_response = fw_service_api.upload_file(update_file)
    if upload_response.status_code != 201:
        raise Exception(f'Failed to upload Firmware to cloud\n'
                        f'Status code: {upload_response.status_code}\n'
                        f'Text: {upload_response.text}')
    ws_log_message('Firmware successfully uploaded to cloud')

    # Publish
    fw_id = upload_response.json().get('id')
    publish_response = fw_service_api.publish(100, [], fw_id)
    if publish_response.status_code != 200:
        raise Exception(f'Failed to publish Firmware\n'
                        f'Status code: {publish_response.status_code}\n'
                        f'Text: {publish_response.text}')
    ws_log_message('Firmware successfully published')


def _generate_incremented_fw_version(src, model, manufacturer):
    # Determine latest version on cloud
    latest = fw_service_api.get_latest_version(model)
    if latest is None:
        latest = '0.0.1.1'
    version = increment_version(latest)

    # Log
    ws_log_message(f'Signing firmware: model={model}, '
                   f'manufacturer={manufacturer}, version={version}')

    # Sign
    signer = FWSigner(
        fw_source=_prepare_emulator_fw(src),
        version=version,
        manufacturer=manufacturer,
        model=model
    )
    output_files = signer.sign()
    return output_files


def _prepare_emulator_fw(src):
    """
    Copy file to temp dir
    """
    dst = os.path.join(settings.SANDBOX_TEMP_DIR, os.path.basename(src))
    if not os.path.exists(dst):
        shutil.copy(src, dst)
    return dst
