#!/usr/bin/env python3
"""Recover compose conflict and finish accounting deploy on production."""
from __future__ import annotations

import base64
import sys
from pathlib import Path

import paramiko

ROOT = Path(__file__).resolve().parents[1]
APP_HOST = "192.168.10.162"
APP_USER = "morteza"
APP_PASS = "Moli@5404"
NGX_HOST = "192.168.10.156"
NGX_USER = "torbatyaruser"
NGX_PASS = "Moli@5404"
REMOTE_DIR = "/home/morteza/torbatyar"


def connect(host: str, user: str, password: str) -> paramiko.SSHClient:
    c = paramiko.SSHClient()
    c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    c.connect(host, username=user, password=password, timeout=30, allow_agent=False, look_for_keys=False)
    return c


def run(client: paramiko.SSHClient, cmd: str, *, sudo: bool = False, password: str = "", timeout: int = 600) -> tuple[int, str]:
    cmd = cmd.replace("\r\n", "\n").replace("\r", "\n").strip() + "\n"
    b64 = base64.b64encode(cmd.encode()).decode()
    if sudo:
        full = f"echo {password!r} | sudo -S -p '' bash -c 'echo {b64} | base64 -d | bash'"
    else:
        full = f"bash -lc 'echo {b64} | base64 -d | bash'"
    print(f"\n>>> {'[sudo] ' if sudo else ''}{cmd[:240].replace(chr(10), ' | ')}...")
    _stdin, stdout, stderr = client.exec_command(full, timeout=timeout, get_pty=True)
    out = stdout.read().decode("utf-8", errors="replace")
    err = stderr.read().decode("utf-8", errors="replace")
    code = stdout.channel.recv_exit_status()
    text = out + (("\n" + err) if err.strip() else "")
    printable = text[-6000:] if len(text) > 6000 else text
    try:
        print(printable)
    except UnicodeEncodeError:
        print(printable.encode("ascii", "replace").decode("ascii"))
    return code, text


def put_text_sudo(client: paramiko.SSHClient, password: str, remote: str, content: str) -> None:
    b64 = base64.b64encode(content.encode()).decode()
    cmd = f"mkdir -p $(dirname {remote!r}) && echo {b64} | base64 -d > {remote!r} && chmod 644 {remote!r}"
    code, _ = run(client, cmd, sudo=True, password=password, timeout=60)
    if code != 0:
        raise RuntimeError(f"failed writing {remote}")


def main() -> int:
    app = connect(APP_HOST, APP_USER, APP_PASS)
    try:
        # Clean docker name conflicts / stuck removals
        run(
            app,
            f"""
set -e
cd {REMOTE_DIR}
docker ps -a --format '{{{{.ID}}}} {{{{.Names}}}}' || true
# remove orphan/conflict core containers by name pattern
for c in $(docker ps -aq --filter name=superapp_core_service); do
  docker rm -f "$c" || true
done
for c in $(docker ps -aq --filter name=61dc7452ee05); do
  docker rm -f "$c" || true
done
docker container prune -f || true
""",
            sudo=True,
            password=APP_PASS,
            timeout=180,
        )

        # Ensure DB + env keys + start services
        code, _ = run(
            app,
            f"""
set -e
cd {REMOTE_DIR}
# upsert accounting env if missing
grep -q '^NEXT_PUBLIC_ACCOUNTING_API_URL=' .env || echo 'NEXT_PUBLIC_ACCOUNTING_API_URL=https://accounting.torbatyar.ir' >> .env
grep -q '^ACCOUNTING_DATABASE_URL=' .env || echo 'ACCOUNTING_DATABASE_URL=postgresql+asyncpg://superapp:superapp_password@postgres:5432/accounting_db' >> .env
grep -q '^ACCOUNTING_DATABASE_URL_SYNC=' .env || echo 'ACCOUNTING_DATABASE_URL_SYNC=postgresql+psycopg://superapp:superapp_password@postgres:5432/accounting_db' >> .env
grep -q '^KEYCLOAK_SERVER_URL=' .env || echo 'KEYCLOAK_SERVER_URL=http://keycloak:8080' >> .env
# force public accounting URL
sed -i 's|^NEXT_PUBLIC_ACCOUNTING_API_URL=.*|NEXT_PUBLIC_ACCOUNTING_API_URL=https://accounting.torbatyar.ir|' .env
sed -i 's|^KEYCLOAK_SERVER_URL=.*|KEYCLOAK_SERVER_URL=http://keycloak:8080|' .env

docker compose up -d postgres
sleep 3
docker compose exec -T postgres psql -U superapp -d postgres -c "SELECT 1 FROM pg_database WHERE datname='accounting_db';" | grep -q 1 \\
  || docker compose exec -T postgres psql -U superapp -d postgres -c "CREATE DATABASE accounting_db;"

docker compose up -d --build --force-recreate accounting-service frontend
sleep 5
docker compose ps
docker compose logs --tail=60 accounting-service
curl -sf http://127.0.0.1:8002/health
echo
curl -sf http://127.0.0.1:3000/accounting >/dev/null && echo FRONTEND_ACCOUNTING_OK || echo FRONTEND_ACCOUNTING_CHECK
""",
            sudo=True,
            password=APP_PASS,
            timeout=1800,
        )
        if code != 0:
            raise RuntimeError("app recover failed")
    finally:
        app.close()

    ngx = connect(NGX_HOST, NGX_USER, NGX_PASS)
    try:
        conf = (ROOT / "infrastructure" / "nginx" / "torbatyar.ir.conf").read_text(encoding="utf-8")
        put_text_sudo(ngx, NGX_PASS, "/etc/nginx/conf.d/torbatyar.ir.conf", conf)
        code, _ = run(
            ngx,
            r"""
set -e
mkdir -p /var/www/html
certbot certonly --webroot -w /var/www/html \
  -d torbatyar.ir -d www.torbatyar.ir \
  -d api.torbatyar.ir -d identity.torbatyar.ir -d auth.torbatyar.ir -d accounting.torbatyar.ir \
  --non-interactive --agree-tos --email support@torbatyar.ir --expand || true
nginx -t
systemctl reload nginx
echo NGINX_RELOADED
curl -skI https://accounting.torbatyar.ir/health | head -n 5 || true
curl -sf https://accounting.torbatyar.ir/health || curl -sk https://accounting.torbatyar.ir/health || true
""",
            sudo=True,
            password=NGX_PASS,
            timeout=300,
        )
        if code != 0:
            raise RuntimeError("nginx step failed")
    finally:
        ngx.close()

    print("\n=== RECOVER DEPLOY FINISHED ===")
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except Exception as exc:
        print(f"FATAL: {exc}", file=sys.stderr)
        raise
