import subprocess
import time
import os

from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.log import getActionLogger

from yunohost.tools import Migration
from yunohost.utils.system import free_space_in_directory, space_used_by_directory

logger = getActionLogger("yunohost.migration")


class MyMigration(Migration):

    "Migrate DBs from Postgresql 11 to 13 after migrating to Bullseye"

    dependencies = ["migrate_to_bullseye"]

    def run(self):

        if (
            os.system(
                'grep -A10 "ynh-deps" /var/lib/dpkg/status | grep -E "Package:|Depends:" | grep -B1 postgresql'
            )
            != 0
        ):
            logger.info("No YunoHost app seem to require postgresql... Skipping!")
            return

        if not self.package_is_installed("postgresql-11"):
            logger.warning(m18n.n("migration_0023_postgresql_11_not_installed"))
            return

        if not self.package_is_installed("postgresql-13"):
            raise YunohostValidationError("migration_0023_postgresql_13_not_installed")

        # Make sure there's a 11 cluster
        try:
            self.runcmd("pg_lsclusters | grep -q '^11 '")
        except Exception:
            logger.warning(
                "It looks like there's not active 11 cluster, so probably don't need to run this migration"
            )
            return

        if not space_used_by_directory(
            "/var/lib/postgresql/11"
        ) > free_space_in_directory("/var/lib/postgresql"):
            raise YunohostValidationError(
                "migration_0023_not_enough_space", path="/var/lib/postgresql/"
            )

        self.runcmd("systemctl stop postgresql")
        time.sleep(3)
        self.runcmd(
            "LC_ALL=C pg_dropcluster --stop 13 main || true"
        )  # We do not trigger an exception if the command fails because that probably means cluster 13 doesn't exists, which is fine because it's created during the pg_upgradecluster)
        time.sleep(3)
        self.runcmd("LC_ALL=C pg_upgradecluster -m upgrade 11 main")
        self.runcmd("LC_ALL=C pg_dropcluster --stop 11 main")
        self.runcmd("systemctl start postgresql")

    def package_is_installed(self, package_name):

        (returncode, out, err) = self.runcmd(
            "dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name),
            raise_on_errors=False,
        )
        return returncode == 0

    def runcmd(self, cmd, raise_on_errors=True):

        logger.debug("Running command: " + cmd)

        p = subprocess.Popen(
            cmd,
            shell=True,
            executable="/bin/bash",
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )

        out, err = p.communicate()
        returncode = p.returncode
        if raise_on_errors and returncode != 0:
            raise YunohostError(
                "Failed to run command '{}'.\nreturncode: {}\nstdout:\n{}\nstderr:\n{}\n".format(
                    cmd, returncode, out, err
                )
            )

        out = out.strip().split(b"\n")
        return (returncode, out, err)