"""Create a consistent database backup.

Examples:
    python scripts/backup_db.py --output backups/publisher.db
    python scripts/backup_db.py --database-url postgresql+asyncpg://... --output backups/publisher.dump

For SQLite the stdlib online backup API is used, so the bot does not need to be
stopped. PostgreSQL backups require ``pg_dump`` on PATH.
"""

from __future__ import annotations

import argparse
import os
import shutil
import sqlite3
import subprocess
import sys
from contextlib import closing
from datetime import UTC, datetime
from pathlib import Path


def _sqlite_path(url: str) -> Path:
    prefix = "sqlite+aiosqlite:///"
    if not url.startswith(prefix):
        raise ValueError("unsupported SQLite URL")
    value = url[len(prefix) :]
    if value == ":memory:" or not value:
        raise ValueError("cannot back up an in-memory SQLite database")
    return Path(value).expanduser()


def _postgres_url(url: str) -> str:
    return url.replace("postgresql+asyncpg://", "postgresql://", 1)


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--database-url",
        default=os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///./data/publisher.db"),
    )
    parser.add_argument(
        "--output",
        default=f"backups/publisher-{datetime.now(UTC).strftime('%Y%m%d-%H%M%S')}",
    )
    args = parser.parse_args()

    output = Path(args.output).expanduser()
    output.parent.mkdir(parents=True, exist_ok=True)
    url = args.database_url.strip()

    if url.startswith("sqlite+aiosqlite:///"):
        source = _sqlite_path(url)
        if not source.exists():
            print(f"database not found: {source}", file=sys.stderr)
            return 2
        target = output if output.suffix else output.with_suffix(".db")
        if target.exists():
            print(f"refusing to overwrite existing file: {target}", file=sys.stderr)
            return 2
        with closing(sqlite3.connect(source)) as src, closing(
            sqlite3.connect(target)
        ) as dst:
            src.backup(dst)
        print(f"SQLite backup written to {target}")
        return 0

    if url.startswith("postgresql+asyncpg://"):
        pg_dump = shutil.which("pg_dump")
        if pg_dump is None:
            print("pg_dump was not found on PATH", file=sys.stderr)
            return 2
        target = output if output.suffix else output.with_suffix(".dump")
        if target.exists():
            print(f"refusing to overwrite existing file: {target}", file=sys.stderr)
            return 2
        subprocess.run(
            [
                pg_dump,
                "--format=custom",
                "--no-owner",
                "--no-privileges",
                f"--file={target}",
                _postgres_url(url),
            ],
            check=True,
        )
        print(f"PostgreSQL backup written to {target}")
        return 0

    print("DATABASE_URL must be sqlite+aiosqlite:/// or postgresql+asyncpg://", file=sys.stderr)
    return 2


if __name__ == "__main__":
    raise SystemExit(main())
