import hashlib
import os
import shutil
import subprocess
from pathlib import Path

from django.conf import settings
from django.core.management.base import CommandError


def pg_config():
    cfg = settings.DATABASES["default"]
    if not cfg["ENGINE"].endswith("postgresql"):
        raise CommandError("This command supports PostgreSQL only.")
    return cfg


def pg_env(cfg):
    env = os.environ.copy()
    if cfg.get("PASSWORD"):
        env["PGPASSWORD"] = str(cfg["PASSWORD"])
    return env


def require_binary(name):
    path = shutil.which(name)
    if not path:
        raise CommandError(f"Required PostgreSQL utility '{name}' was not found in PATH.")
    return path


def base_connection_args(cfg):
    args = []
    if cfg.get("HOST"):
        args += ["--host", str(cfg["HOST"])]
    if cfg.get("PORT"):
        args += ["--port", str(cfg["PORT"])]
    if cfg.get("USER"):
        args += ["--username", str(cfg["USER"])]
    return args


def sha256_file(path):
    digest = hashlib.sha256()
    with Path(path).open("rb") as fh:
        for chunk in iter(lambda: fh.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def run_checked(args, *, env=None):
    result = subprocess.run(args, env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if result.returncode:
        message = result.stderr.strip() or result.stdout.strip() or "PostgreSQL utility failed."
        raise CommandError(message)
    return result


def encryption_password():
    from django.conf import settings
    password = str(getattr(settings, "BACKUP_ENCRYPTION_PASSWORD", "") or "")
    if getattr(settings, "BACKUP_REQUIRE_ENCRYPTION", False) and not password:
        raise CommandError(
            "BACKUP_REQUIRE_ENCRYPTION is enabled but BACKUP_ENCRYPTION_PASSWORD is empty. "
            "Set the secret before creating/verifying/restoring production backups."
        )
    return password


def encrypt_backup_file(source_path):
    """Encrypt a backup with OpenSSL AES-256-CBC/PBKDF2 and remove plaintext."""
    source = Path(source_path)
    password = encryption_password()
    if not password:
        return source
    openssl = require_binary("openssl")
    target = Path(str(source) + ".enc")
    env = os.environ.copy()
    env["HRPAY_BACKUP_SECRET"] = password
    run_checked([
        openssl, "enc", "-aes-256-cbc", "-salt", "-pbkdf2", "-iter", "200000",
        "-in", str(source), "-out", str(target), "-pass", "env:HRPAY_BACKUP_SECRET",
    ], env=env)
    source.unlink(missing_ok=True)
    return target


def decrypt_backup_to(source_path, target_path):
    source = Path(source_path)
    target = Path(target_path)
    if source.suffix != ".enc":
        shutil.copy2(source, target)
        return target
    password = encryption_password()
    if not password:
        raise CommandError("Encrypted backup requires BACKUP_ENCRYPTION_PASSWORD.")
    openssl = require_binary("openssl")
    env = os.environ.copy()
    env["HRPAY_BACKUP_SECRET"] = password
    run_checked([
        openssl, "enc", "-d", "-aes-256-cbc", "-pbkdf2", "-iter", "200000",
        "-in", str(source), "-out", str(target), "-pass", "env:HRPAY_BACKUP_SECRET",
    ], env=env)
    return target
