import ast
import datetime
import git
import os
import uuid
import re
import subprocess
import shutil

from ..worker import Worker, WorkTypes, SCENARIOS_DIR

DIR_PATH = os.path.dirname(os.path.realpath(__file__))
DRIVING_ROOT = os.popen('git rev-parse --show-toplevel').read().strip()
SCENARIOS_DIR = os.path.join(DRIVING_ROOT, 'sim/scenario_editor/web/backend/scenarios')

# start worker thread
WORKER = Worker()
WORKER.start()


def make_error(error_msg, status_code):
    return ({"statusCode": status_code, "message": error_msg}, status_code)


def scenario_sim(scenario, extra_sim_args):
    work_id = str(uuid.uuid4())
    with WORKER.results_lock:
        WORKER.results[work_id] = None
    WORKER.queue.put_nowait({
        "type": WorkTypes.RUN_SCENARIO.value,
        "work_id": work_id,
        "scenario": scenario,
        "extra_sim_args": extra_sim_args})
    return work_id


def check_worker_results(work_id):
    with WORKER.results_lock:
        if work_id not in WORKER.results:
            return make_error("work id %s does not exist" % work_id, 404)
        if WORKER.results[work_id] is None:
            return {"status": "WAITING"}, 202
    response = WORKER.results[work_id]
    response["status"] = "COMPLETE"
    return response


def pbtxt_to_bob(scenario):
    pbtxt_path = os.path.join(SCENARIOS_DIR, datetime.datetime.now().strftime("%Y%m%d%H%M%S") + ".pbtxt")
    with open(pbtxt_path, "w") as f:
        f.write(scenario)
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:pbtxt_to_bob",
        "--",
        pbtxt_path], stdout=subprocess.PIPE)
    return result.stdout.decode().strip()


def enum_pbtxt(scenario):
    pbtxt_path = os.path.join(SCENARIOS_DIR, datetime.datetime.now().strftime("%Y%m%d%H%M%S") + ".pbtxt")
    with open(pbtxt_path, "w") as f:
        f.write(scenario)
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:enum_pbtxt",
        "--",
        pbtxt_path], stdout=subprocess.PIPE)
    pbtxt = result.stdout.decode().strip()
    return pbtxt


def git_hash():
    repo = git.Repo(DIR_PATH, search_parent_directories=True)
    return repo.head.object.hexsha


def git_branch():
    try:
        repo = git.Repo(DIR_PATH, search_parent_directories=True)
        branch = repo.active_branch.name
    except (TypeError) as e:
        branch = 'DETACHED_' + repo.head.object.hexsha
    return branch

def convert_to_smart_log(chum_uri):
    work_id = str(uuid.uuid4())
    with WORKER.results_lock:
        WORKER.results[work_id] = None
    work = {
        "type": WorkTypes.COVERT_TO_SMART_LOG.value,
        "work_id": work_id,
        "chum_uri": chum_uri}
    WORKER.queue.put_nowait(work)
    return work_id

def load_agent_database():
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:load_agent_database"], stdout=subprocess.PIPE)
    return result.stdout.decode().strip()

def server_open_file(file_name):
    if not os.path.exists(file_name):
        return make_error("Server file doesn't exist", 404)
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:pbtxt_to_bob",
        "--",
    file_name], stdout=subprocess.PIPE)
    return result.stdout.decode().strip()

def server_save_file(file_name, scenario):
    try:
        scenario = enum_pbtxt(scenario)  # enum numbers to readable strings
        with open(file_name, "w") as f:
            f.write(scenario)
    except IOError as error:
        return make_error("Server failed to write file %s : %s" % (file_name, error), 404)
    except:
        return make_error("Server failed to write file " + file_name, 404)
    return "success"

def junction_info(junction_ids, **kwargs):
    command = [
        "bazel",
        "run",
        ("//sim/scenario_editor/web/backend/sim_service/scripts/junctions" +
        ":junctions_from_ids"),
        "--"
    ]
    command += [
        kwargs.get("zrn_gitsha", ""),
        kwargs.get("zrn_name", ""),
        kwargs.get("zrn_hash", ""),
        kwargs.get("zrn_map", ""),
    ]
    command += junction_ids
    result = subprocess.run(command, stdout=subprocess.PIPE)
    raw_junction_info = result.stdout.decode()
    junction_info = ast.literal_eval(raw_junction_info)
    return junction_info

def junction_from_position(position_x, position_y, **kwargs):
    command = [
        "bazel",
        "run",
        ("//sim/scenario_editor/web/backend/sim_service/scripts/junctions" +
        ":junction_from_position"),
        "--"
    ]
    command += [
        kwargs.get("zrn_gitsha", ""),
        kwargs.get("zrn_name", ""),
        kwargs.get("zrn_hash", ""),
        kwargs.get("zrn_map", ""),
    ]
    command += [str(position_x), str(position_y)]
    result = subprocess.run(command, stdout=subprocess.PIPE)
    raw_junction_info = result.stdout.decode()
    junction_info = ast.literal_eval(raw_junction_info)
    return junction_info[0]

def build_route_position_list(zrn_gitsha, zrn_name, entity_route):
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts/route:build_route_position_list",
        "--", zrn_gitsha, zrn_name, entity_route], stdout=subprocess.PIPE)
    raw_route_segments = result.stdout.decode()
    route_segments = ast.literal_eval(raw_route_segments)
    return route_segments

def copy_chum_to_nautilus(chum_uri):
    def set_nautilus_permissions(_nautilus_dir):
        cmd = 'chmod -R 777 %s' % _nautilus_dir
        print('setting nautilus access permissions: ', cmd)
        os.system(cmd)

    # get variant & chum root from chum URI
    # example chum URI: chum://kitt_sim@1234567889.999998976-1234567902.714998992?i=$empty,/tmp/scenario_editor/2021-09&v=7e2dd630-7afe-11eb-be62-04d4c452b3a8
    match = re.search(r"i=([\w\/=,$_-]+)&v=([\w-]+)", chum_uri)
    if not match:
        msg = 'failed to find directory & variant from chum URI: %s' % chum_uri
        print(msg)
        return make_error(msg, 404)

    variant = match.group(2)
    local_chum_root = match.group(1).replace('$empty','').replace(',','')  # strip "$empty,"
    source = os.path.join(local_chum_root, '_vars', variant)

    year_week = datetime.datetime.now().strftime('%Y-%U')
    user = os.getenv('USER', 'unknown_user')
    nautilus_scenario_editor_dir = '/mnt/nautilus_rw/scenario_editor'
    nautilus_week_dir = os.path.join(nautilus_scenario_editor_dir, year_week)
    nautilus_user_dir = os.path.join(nautilus_week_dir, user)
    destination = os.path.join(nautilus_user_dir, '_vars')

    for nautilus_dir in [nautilus_week_dir, nautilus_user_dir, destination]:
        # if needed, create each layer of nautilus chum directory & set permissions
        if not os.path.exists(nautilus_dir):
            print('creating nautilus directory: ', nautilus_dir)
            os.makedirs(nautilus_dir)
            set_nautilus_permissions(nautilus_dir)

    chum_uri_nautilus = ""
    if os.path.exists(source) and os.path.exists(destination):
        # move chum from local to nautilus
        shutil.move(source, destination)
        set_nautilus_permissions(os.path.dirname(destination))

        # get chum URI
        dir_path = os.path.dirname(os.path.abspath(__file__))
        cmd_get_chum_uri = os.path.join(dir_path, "..", "scripts", "get_chum_uri.sh")
        result = subprocess.run([cmd_get_chum_uri, nautilus_user_dir, variant], stdout=subprocess.PIPE)
        chum_uri_nautilus = result.stdout.decode().strip()
        print('chum URI: ', chum_uri_nautilus)
    else:
        msg = 'failed to copy chum to nautilus, either source or destination ' \
                'path did not exist: %s or %s' % (source, destination)
        return make_error(msg, 404)

    return chum_uri_nautilus

def create_log_test(data_id):
    work_id = str(uuid.uuid4())
    with WORKER.results_lock:
        WORKER.results[work_id] = None
    work = {
        "type": WorkTypes.CREATE_LOG_TEST.value,
        "work_id": work_id,
        "data_id": data_id}
    WORKER.queue.put_nowait(work)
    return work_id

def zrn_archive_best_fit(zrn_map, zrn_hash):
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:zrn_archive_migration",
        "--", zrn_map, zrn_hash], stdout=subprocess.PIPE)
    canonical_name, gitsha = result.stdout.decode().strip().split(" ")
    return { "gitsha": gitsha, "canonicalName": canonical_name }

def populate_trajectory_set(zrn_gitsha, zrn_name, prediction_override_proto, obstacle_type):
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts/prediction_override:populate_trajectory_set",
        "--", zrn_gitsha, zrn_name, prediction_override_proto, obstacle_type], stdout=subprocess.PIPE)
    trajectory_set_result = result.stdout.decode()
    return trajectory_set_result

def zrn_unique_gitsha(canonical_name, gitsha):
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts:zrn_unique_gitsha",
        "--", canonical_name, gitsha], stdout=subprocess.PIPE)
    gitsha = result.stdout.decode().strip()
    if result.returncode:
        return make_error("failed to make unique gitsha", 503)
    return gitsha

def visualize_trajectory(zrn_gitsha, zrn_name, route_frame_trajectory_proto, entity_position_proto):
    result = subprocess.run([
        "bazel",
        "run",
        "//sim/scenario_editor/web/backend/sim_service/scripts/prediction_override:visualize_trajectory",
        "--", zrn_gitsha, zrn_name, route_frame_trajectory_proto, entity_position_proto], stdout=subprocess.PIPE)
    trajectories = result.stdout.decode()
    return trajectories
