import os
import click
import shutil
import subprocess

from wurker.settings import conf

from wurker.cli.base import BaseCommand


class ServiceCommand(BaseCommand):
    """Class to encapsulate 'service' command."""

    def __init__(self, logger=None):
        BaseCommand.__init__(self, logger=logger, check_schema=True)

        # Service configs generated to the Wurker etc dir
        self.app_dir = conf.app_root
        self.etc_dir = conf.etc_root


    def run(self, service:str, virtualenv:str='', workingdir:str='', **kwargs):
        """Configure systemd services, or systemd and nginx configs."""

        if not virtualenv:
            raise ValueError("Required '--virtualenv' option must be a string")
        if not workingdir:
            raise ValueError("Required '--workingdir' option must be a string")

        click.echo(f"Configuring '{service}' service(s) from: '{self.etc_dir}'...")

        if service == "wurker":
            return self.make_wurker(virtualenv=virtualenv, workingdir=workingdir)
        elif service == "ui":
            return self.make_ui(virtualenv=virtualenv, workingdir=workingdir, **kwargs)
        else:
            raise ValueError(f"Invalid service option: '{service}'")


    def make_wurker(self, virtualenv:str='', workingdir:str=''):
        """Make the 'wurker' systemd service config."""

        # Render systemd conf
        self._render_systemd_conf("wurker", virtualenv=virtualenv, workingdir=workingdir)


    def make_ui(
            self,
            virtualenv:str='',
            workingdir:str='',
            domain:str='',
            workers:int=0,
            socket:str='',
            ssl_dir:str='',
            ssl_certs:list=[],
    ):
        """Make the 'wurker-ui' systemd service config."""

        if not domain:
            raise ValueError("Required '--domain' option must be a string")
        if not workers:
            raise ValueError("Required '--workers' option must be a string")
        if not socket:
            raise ValueError("Required '--socket' option must be a string")
        if not ssl_dir:
            raise ValueError("Required '--ssl-dir' option must be a string")
        if not ssl_certs:
            raise ValueError("Required '--ssl-certs' option must be a string")

        # Render systemd conf
        self._render_systemd_conf(
            "ui",
            virtualenv=virtualenv,
            workingdir=workingdir,
            workers=workers,
            socket=socket,
        )

        # Also render the nginx vhost file
        self._render_nginx_conf(domain, socket, ssl_dir, ssl_certs)

    def _get_primary_group(self):
        result = subprocess.run(["id", "-gn"], capture_output=True, text=True, check=True)
        return result.stdout.strip()

    def _render_systemd_conf(self, service:str, virtualenv:str='', workingdir:str='', workers:int=0, socket:str=''):
        """Render the systemd service file for wurker or wurker-ui."""

        user = os.getenv("USER")
        group = self._get_primary_group()
        if not user or not group:
            raise ValueError(f"Could not obtain USER ({user}) or GROUP ({group})")

        systemd_skel_dir = os.path.join(self.etc_dir, "systemd")
        service_conf = "wurker.service" if service == "wurker" else "wurker-ui.service"
        service_skel = f"{service_conf}.skel"

        skel = None
        with open(os.path.join(systemd_skel_dir, service_skel), 'r') as f:
            skel = f.read()

        if not skel:
            raise Exception(f"Systemd template file empty: '{service_skel}'")

        skel = skel.replace("{{ user }}", user)
        skel = skel.replace("{{ group }}", group)
        skel = skel.replace("{{ app_root }}", conf.app_root)
        skel = skel.replace("{{ virtualenv }}", virtualenv)
        skel = skel.replace("{{ workingdir }}", workingdir)

        if service == "ui":
            skel = skel.replace("{{ workers }}", str(workers))
            skel = skel.replace("{{ socket }}", socket)

        service_conf = os.path.join(systemd_skel_dir, service_conf)
        with open(service_conf, 'w') as f:
            f.write(skel)

        click.echo(f"  . Rendered '{service}' systemd service file from template: '{service_conf}'")

        return service_conf


    def _render_nginx_conf(self, domain:str, socket:str, ssl_dir:str, ssl_certs:list):
        """Render the nginx vhost file for wurker-ui."""

        # Vhost stuff
        vhost_skel_dir = os.path.join(self.etc_dir, "nginx")
        vhost_conf = f"{domain}.conf"
        vhost_skel = "wurker-ui.conf.skel"

        # SSL certs stuff
        ssl_dir = os.path.join(self.etc_dir, "nginx", "ssl")
        if not os.path.exists(ssl_dir):
            os.makedirs(ssl_dir, mode=0o755)

        # Generate self-signed certs if certs don't exist
        src_certs = [c if c.startswith('/') else os.path.join(ssl_dir, c) for c in ssl_certs]

        ssl_crt, ssl_key = ssl_certs
        if any((not os.path.exists(c)) for c in src_certs):
            ssl_crt, ssl_key = self._create_certs(ssl_dir, ssl_crt, ssl_key)

        # Load the skel template
        skel = None
        with open(os.path.join(vhost_skel_dir, vhost_skel), 'r') as f:
            skel = f.read()

        if not skel:
            raise Exception(f"Nginx template file empty: '{vhost_skel}'")

        skel = skel.replace("{{ domain }}", domain)
        skel = skel.replace("{{ socket }}", socket)
        skel = skel.replace("{{ ssl_certificate }}", ssl_crt)
        skel = skel.replace("{{ ssl_certificate_key }}", ssl_key)

        vhost_conf = os.path.join(vhost_skel_dir, vhost_conf)
        with open(vhost_conf, 'w') as f:
            f.write(skel)

        click.echo(f"  . Rendered nginx vhost file from template: '{vhost_conf}'")

        return vhost_conf


    def _create_certs(self, ssl_dir, ssl_crt, ssl_key):
        """Create SSL certs for nginx."""

        click.echo(f"  . Creating self-signed SSL certificates in: '{ssl_dir}'")

        if not os.path.exists(ssl_dir):
            os.makedirs(ssl_dir)

        cmd = (
            "openssl req -nodes -newkey rsa:2048 -new -x509 "
            f"-out {ssl_crt} -keyout {ssl_key} "
            "-subj '/C=US/ST=FL/L=Miami/O=Wurkzen/OU=Wurker/CN=wurkzen.com/emailAddress=info@wurkzen.com'"
        )

        subprocess.run(cmd, shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

        return os.path.join(ssl_dir, ssl_crt), os.path.join(ssl_dir, ssl_key)
