# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import multiprocessing
import os
import time
import webbrowser
from dataclasses import dataclass
from threading import Timer

from gecko_taskgraph.target_tasks import filter_by_uncommon_try_tasks

from tryselect.cli import BaseTryParser
from tryselect.push import (
    check_working_directory,
    generate_try_task_config,
    push_to_try,
)
from tryselect.tasks import generate_tasks

here = os.path.abspath(os.path.dirname(__file__))


@dataclass
class ChooserConfig:
    use_artifact: bool = False
    pernosco_active: bool = False
    rebuild_multiplier: int = 1
    priority_preset: bool = False


class ChooserParser(BaseTryParser):
    name = "chooser"
    arguments = []
    common_groups = ["push", "task"]
    task_configs = [
        "artifact",
        "browsertime",
        "chemspill-prio",
        "disable-pgo",
        "env",
        "existing-tasks",
        "extensions",
        "gecko-profile",
        "path",
        "pernosco",
        "rebuild",
        "worker-overrides",
    ]


def run(
    metrics,
    update=False,
    query=None,
    try_config_params=None,
    full=False,
    parameters=None,
    save=False,
    preset=None,
    mod_presets=False,
    stage_changes=False,
    dry_run=False,
    message="{msg}",
    closed_tree=False,
    push_to_vcs=False,
):
    from .app import create_application

    push = not stage_changes and not dry_run
    check_working_directory(push)

    metrics.mach_try.taskgraph_generation_duration.start()
    tg = generate_tasks(parameters, full)
    metrics.mach_try.taskgraph_generation_duration.stop()

    metrics.mach_try.task_filtering_duration.start()
    # Remove tasks that are not to be shown unless `--full` is specified.
    if not full:
        excluded_tasks = [
            label
            for label in tg.tasks.keys()
            if not filter_by_uncommon_try_tasks(label)
        ]
        for task in excluded_tasks:
            tg.tasks.pop(task)

    metrics.mach_try.task_filtering_duration.stop()

    queue = multiprocessing.Queue()

    try_config_params = try_config_params or {}
    try_task_config = try_config_params.setdefault("try_task_config", {})
    rebuild_multiplier, priority_preset = resolve_large_push_context(try_task_config)
    config = ChooserConfig(
        use_artifact=bool(try_task_config.get("use-artifact-builds")),
        pernosco_active=bool(try_task_config.get("pernosco")),
        rebuild_multiplier=rebuild_multiplier,
        priority_preset=priority_preset,
    )

    if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
        # we are in the reloader process, don't open the browser or do any try stuff
        app = create_application(tg, queue, config)
        app.run()
        return

    # give app a second to start before opening the browser
    url = "http://127.0.0.1:5000"
    Timer(1, lambda: webbrowser.open(url)).start()
    print(f"Starting trychooser on {url}")
    process = multiprocessing.Process(
        target=create_and_run_application,
        args=(tg, queue, config),
    )
    process.start()

    metrics.mach_try.interactive_duration.start()
    result = queue.get()
    metrics.mach_try.interactive_duration.stop()

    # Allow the close page to render before terminating the process.
    time.sleep(1)
    process.terminate()

    selected = result["tasks"]
    use_artifact = result["use_artifact"]
    if not selected:
        print("no tasks selected")
        return

    resolve_artifact_state(try_task_config, config.use_artifact, use_artifact)

    msg = f"Try Chooser Enhanced ({len(selected)} tasks selected)"
    return push_to_try(
        "chooser",
        message.format(msg=msg),
        metrics,
        try_task_config=generate_try_task_config(
            "chooser", selected, params=try_config_params
        ),
        stage_changes=stage_changes,
        dry_run=dry_run,
        closed_tree=closed_tree,
        push_to_vcs=push_to_vcs,
    )


def resolve_large_push_context(try_task_config):
    """Match push.generate_try_task_config's math for the chooser warning.

    Returns (rebuild_multiplier, priority_preset). rebuild_multiplier is the
    per-label rebuild count, defaulting to 1 when ``rebuild`` is absent,
    mirroring push.py. priority_preset is True when an explicit priority was
    passed in, since push.py skips the deprioritization prompt in that case.
    """
    rebuild_multiplier = try_task_config.get("rebuild", 1)
    priority_preset = "priority" in try_task_config
    return rebuild_multiplier, priority_preset


def resolve_artifact_state(try_task_config, initial_use_artifact, use_artifact):
    if use_artifact:
        try_task_config["use-artifact-builds"] = True
        try_task_config["disable-pgo"] = True
        return

    try_task_config.pop("use-artifact-builds", None)
    # --artifact (task_config.Artifact) sets disable-pgo as a consequence
    # of enabling artifact builds. If we entered the chooser with artifact
    # on and the user toggled it off, clear disable-pgo too. An explicit
    # --disable-pgo run leaves initial_use_artifact=False and is preserved.
    if initial_use_artifact:
        try_task_config.pop("disable-pgo", None)


def create_and_run_application(tg, queue: multiprocessing.Queue, config=None):
    from .app import create_application

    app = create_application(tg, queue, config)
    app.run()
