#!/usr/bin/env python3

import argparse
import logging
import os
import time
import traceback

from shutil import rmtree

from firmware_update import download_file, get_installation_candidate
from rauc import get_rauc_slots, should_download_bundle, get_target_slot, get_booted_slot, rauc_install, switch_to_slot
from utils import run_command

BOARD_INFO_FILE = '/sys/devices/platform/board/info'
HARDWARE_NAME = 'mrc-nanopi-neo4'
ROOTFS_SLOT = 'rootfs'

logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG)
logger = logging.getLogger('mrc-drive-updater')


def get_device_id():
    if not os.path.isfile(BOARD_INFO_FILE):
        raise RuntimeError('Cannot open board info file: {}'.format(BOARD_INFO_FILE))
    with open(BOARD_INFO_FILE, 'r') as f:
        for line in f:
            splitted = line.split()
            if splitted[0] == 'Serial':
                return splitted[2]
    raise RuntimeError('Cannot find serial number of device')


def get_user_agent(rauc_slots):
    slot = get_booted_slot(rauc_slots)
    user_agent = HARDWARE_NAME + '-' + slot.slotclass + '/'
    version = slot.version if slot.version else 'None'
    return user_agent + version


def download_and_install(firmware):
    try:
        download_dir = '/data/updates'
        rmtree(download_dir, ignore_errors=True)
        os.makedirs(download_dir, mode=0o755, exist_ok=True)

        logger.debug('Download firmware update to dir: {}'.format(download_dir))
        file_path = os.path.join(download_dir, HARDWARE_NAME + '-' + firmware.version + '.raucb')
        download_file(firmware.url, file_path, firmware.md5sum)

        return rauc_install(file_path)
    finally:
        os.remove(file_path)


def do_firmware_update():
    logger.info('Start firmware update')

    slots = get_rauc_slots()

    firmware = get_installation_candidate(get_user_agent(slots), HARDWARE_NAME, get_device_id(), ROOTFS_SLOT)
    if not firmware:
        logger.info('No firmware update found, quit')
        return
    logger.info('Installation candidate: {}'.format(firmware))

    should_download = should_download_bundle(firmware.version, slots)
    target_slot = get_target_slot(firmware.version, slots)
    booted_slot = get_booted_slot(slots)

    success = False
    if should_download:
        success = download_and_install(firmware)
    elif booted_slot.version != firmware.version and target_slot:
        success = switch_to_slot(target_slot)
    else:
        logger.warning('No slot switch needed')

    if success:
        logger.info('Firmware update done, reboot')
        run_command('systemctl stop mrc-drive'.split())
        run_command('reboot now'.split())


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--update_interval', type=int, required=False, default=300,
                        help='Update interval in seconds')

    args = parser.parse_args()

    while True:
        try:
            logger.info('Start update')
            do_firmware_update()
        except:
            logger.error(traceback.format_exc())

        time.sleep(args.update_interval)


if __name__ == '__main__':
    main()
