"""Common logic for Mobile Assets of type .APK package file AAB package file and IPA .
This module takes care of preparing the application file and calling the create mobile scan API.
"""
import io
import click
import itertools

from typing import List

from ostorlab.cli.ci_scan.run import run
from ostorlab.apis.runners import authenticated_runner
from ostorlab.apis.runners import runner as base_runner
from ostorlab.apis import scan_create as scan_create_api
from ostorlab.apis import test_credentials_create as test_credentials_create_api


def _prepare_test_credentials(ctx: click.core.Context) -> List[test_credentials_create_api.TestCredential]:
    test_credentials_login = ctx.obj['test_credentials']['test_credentials_login']
    test_credentials_password = ctx.obj['test_credentials']['test_credentials_password']
    test_credentials_url = ctx.obj['test_credentials']['test_credentials_url']
    test_credentials_role = ctx.obj['test_credentials']['test_credentials_role']
    test_credentials_name = ctx.obj['test_credentials']['test_credentials_name']
    test_credentials_value = ctx.obj['test_credentials']['test_credentials_value']

    credentials = []
    for login, password, role, url in itertools.zip_longest(
            test_credentials_login,
            test_credentials_password,
            test_credentials_role,
            test_credentials_url):
        credentials.append(test_credentials_create_api.TestCredentialLogin(
            login=login,
            password=password,
            role=role,
            url=url
        ))

    if test_credentials_name:
        credentials.append(test_credentials_create_api.TestCredentialCustom(
            values=dict(itertools.zip_longest(test_credentials_name, test_credentials_value))))

    return credentials


def run_mobile_scan(ctx: click.core.Context, file: io.FileIO, asset_type: scan_create_api.MobileAssetType) -> None:
    """Create scan for mobile application package file."""
    ci_logger = ctx.obj['ci_logger']
    if ctx.obj.get('api_key'):
        scan_profile = ctx.obj['scan_profile']
        title = ctx.obj['title']
        break_on_risk_rating = ctx.obj['break_on_risk_rating']
        max_wait_minutes = ctx.obj['max_wait_minutes']
        runner = authenticated_runner.AuthenticatedAPIRunner(api_key=ctx.obj.get('api_key'))
        try:

            test_credentials = _prepare_test_credentials(ctx)
            if test_credentials:
                credential_ids = _create_test_credentials(test_credentials, runner)
                ci_logger.info(f'Created test credentials {", ".join(str(t) for t in test_credentials)} successfully')
            else:
                credential_ids = []

            ci_logger.info(f'creating scan `{title}` with profile `{scan_profile}` for `{asset_type}`')
            scan_id = _create_scan(title, scan_profile, asset_type, file, credential_ids, runner)

            ci_logger.output(name='scan_id', value=scan_id)
            ci_logger.info(f'Scan created with id {scan_id}.')

            if break_on_risk_rating is not None:
                run.apply_break_scan_risk_rating(break_on_risk_rating, scan_id, max_wait_minutes, runner, ci_logger)

        except base_runner.ResponseError as e:
            ci_logger.error(f'Could not start the scan. {e}')
            raise click.exceptions.Exit(2) from e
        except TimeoutError:
            ci_logger.error('The scan is still running.')
            raise click.exceptions.Exit(2) from None
    else:
        ci_logger.error('API key not not provided.')
        raise click.exceptions.Exit(2) from None


def _create_scan(title, scan_profile, asset_type, file, credential_ids, runner):
    scan_result = runner.execute(
        scan_create_api.CreateMobileScanAPIRequest(title=title,
                                                   asset_type=asset_type,
                                                   scan_profile=scan_profile,
                                                   application=file,
                                                   test_credential_ids=credential_ids
                                                   ))
    scan_id = scan_result.get('data').get('createMobileScan').get('scan').get('id')
    return scan_id


def _create_test_credentials(test_credentials, runner):
    credential_ids = []
    for test_credential in test_credentials:
        test_credential_result = runner.execute(
            test_credentials_create_api.CreateTestCredentialAPIRequest(test_credential=test_credential))
        credential_ids.append(
            test_credential_result.get('data').get('createTestCredentials').get('testCredentials').get('id'))
    return credential_ids
