#!/usr/bin/env python3

import os
import subprocess
import sys

# Support dashes in command names
COMMAND_TO_METHOD = {}
METHOD_TO_COMMAND = {method: command for command, method in COMMAND_TO_METHOD.items()}

WAIT_FOR_PROCESS = "wait_for_process"


class ProdHelper:
    def __init__(self):
        self.last_exit_status = 0

    # Core logic
    def call(self, *args, **kwargs):
        if not args:
            return self.compose(*args, **kwargs)

        command, rest = args[0], args[1:]
        method_name = COMMAND_TO_METHOD.get(command, command)
        method = getattr(self, method_name, None)
        if callable(method) and not method_name.startswith("_") and method_name != "call":
            return method(*rest, **kwargs)
        return self.compose(*args, **kwargs)

    def compose(self, *args, **kwargs):
        command = self._compose_command(*args, **kwargs)
        if not kwargs.get("silent"):
            print(f"Running: {command}")

        if kwargs.get("execution_mode") == WAIT_FOR_PROCESS:
            self._wait_for_process_with_logging(command)
        else:
            os.execv("/bin/sh", ["/bin/sh", "-c", command])

    # Primary command wrappers
    def up(self, *args, **kwargs):
        self.compose("up", "--remove-orphans", *args, **kwargs)

    def down(self, *args, **kwargs):
        self.compose("down", "--remove-orphans", *args, **kwargs)

    def logs(self, *args, **kwargs):
        self.compose("logs", "-f", *args, **kwargs)

    def ps(self, *args, **kwargs):
        self.compose("ps", *args, **kwargs)

    def bash_completions(self, *args, **kwargs):
        completions = sorted(
            METHOD_TO_COMMAND.get(name, name)
            for name in vars(ProdHelper)
            if not name.startswith("_") and name != "call" and callable(getattr(ProdHelper, name))
        )
        print(" ".join(completions))

    # Private helpers
    def _wait_for_process_with_logging(self, command):
        process = subprocess.Popen(
            ["/bin/sh", "-c", f"{command} 2>&1"],
            stdout=subprocess.PIPE,
            text=True,
        )
        for line in process.stdout:
            print(line, end="")
        process.wait()
        self.last_exit_status = process.returncode

    def _compose_command(self, *args, **kwargs):
        environment = kwargs.get("environment", "production")
        return f"cd {self._project_root()} && docker compose -f docker-compose.{environment}.yml {' '.join(args)}"

    def _project_root(self):
        return os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))


if __name__ == "__main__":
    ProdHelper().call(*sys.argv[1:])
