#!/usr/bin/env python3

import argparse
import json
import os
import requests
import requests.adapters
import requests.exceptions
import simplejson.errors
import urllib3.util.retry

EXAMPLE_SIMULATIONS_RUNS_FILENAME = os.path.join(os.path.dirname(__file__),
                                                 '..', 'apps', 'dispatch', 'src', 'app', 'components',
                                                 'simulations', 'browse', 'example-simulations.{}.json')


def get_validate_endpoint(biosimulations_deployment, run_id, validate_simulation_results_data=True):
    """ Get the endpoint for checking the status of a simulation run

    Args:
        biosimulations_deployment (:obj:`str`): deployment to use to check the status of each run
            (``dev``, ``prod``, or ``local``)
        run_id (:obj:`str`): id of the simulation run
        validate_simulation_results_data (:obj:`bool`, optional): whether to validate
            the data of the simulation results

    Returns:
        :obj:`str`: endpoint for checking the status of each simulation run
    """
    if biosimulations_deployment == 'local':
        endpoint = 'http://localhost:3333'
    elif biosimulations_deployment == 'dev':
        endpoint = 'https://api.biosimulations.dev'
    else:
        endpoint = 'https://api.biosimulations.org'

    endpoint += '/runs/{}/validate'.format(run_id)

    if validate_simulation_results_data:
        endpoint += "?validateSimulationResultsData=true"

    return endpoint


def get_run_url(biosimulations_deployment, id):
    """ Get the URL for a simulation run

    Args:
        biosimulations_deployment (:obj:`str`): deployment to use to check the status of each run
            (``dev``, ``prod``, or ``local``)

    Returns:
        :obj:`str`: URL for a simulation run
    """
    if biosimulations_deployment == 'local':
        return 'http://localhost:3333/runs/{}'.format(id)
    elif biosimulations_deployment == 'dev':
        return 'https://run.biosimulations.dev/runs/{}'.format(id)
    else:
        return 'https://run.biosimulations.org/runs/{}'.format(id)


def get_failed_runs(examples_deployment, biosimulations_deployment, validate_simulation_results_data=True, example_names=None):
    """ Get a list of any example simulation runs that didn't succeed

    Args:
        examples_deployment (:obj:`str`): example set to test (``dev`` or ``prod``)
        biosimulations_deployment (:obj:`str`): deployment to use to check the status of each run
            (``dev``, ``prod``, or ``local``)
        validate_simulation_results_data (:obj:`bool`, optional): whether to validate
            the data of the simulation results
        example_names (:obj:`list` of :obj:`str`, optional): names of examples to check; default: check all examples

    Returns:
        :obj:`list` of :obj:`dict`: simulation runs that were checked
        :obj:`list` of :obj:`str`: ids and names of failed simulation runs
    """

    # get name of file with runs
    filename = EXAMPLE_SIMULATIONS_RUNS_FILENAME.format(examples_deployment)

    # read simulation runs
    with open(filename, 'r') as file:
        runs = json.load(file)

    if example_names is not None:
        runs = list(filter(lambda run: run['name'] in example_names, runs))

    # check the status of each run
    failures = []
    for run in runs:
        validate_endpoint = get_validate_endpoint(biosimulations_deployment, run['id'])
        response = requests.get(validate_endpoint)
        try:
            response.raise_for_status()
        except requests.exceptions.RequestException:
            try:
                error = response.json()['error'][0]['detail']
            except simplejson.errors.JSONDecodeError:
                error = 'Valiation failed'

            failures.append('<details><summary><b>{} (<a href="{}" target="biosimulations">{}</a>)</b></summary><br/>\n{}\n</details>'.format(
                run['name'], get_run_url(biosimulations_deployment, run['id']), run['id'], error,
            ))

    # return list of failed runs
    return runs, failures


if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Check that all of the example simulation runs succeeded.'
    )
    parser.add_argument(
        '--runbiosimulations-deployment',
        type=str,
        default='dev',
        help='BioSimulations deployment which for projects should be checked (`dev`, `prod`, `local`). Default: `dev`.'
    )
    parser.add_argument(
        '--example', type=str, nargs='*',
        help='Names of the example simulations to execute. Default: execute all simulations.',
        default=None, dest='example_names',
    )
    args = parser.parse_args()

    examples_deployment = args.runbiosimulations_deployment
    if examples_deployment == 'local':
        examples_deployment = 'dev'
    runs, failures = get_failed_runs(examples_deployment, args.runbiosimulations_deployment, example_names=args.example_names)

    if failures:
        msg = 'The following {} example simulation runs did not succeed:\n\n{}'.format(len(failures), '\n\n'.join(failures))
        raise SystemExit(msg)
    else:
        print('All {} example simulation runs succeeded!'.format(len(runs)))
