#!/usr/bin/env python3
"""
PostgreSQL Backup Service for WebApp.

- Creates pg_dump backup
- Compresses with gzip
- Uploads to S3 FirstVDS
- Rotates old backups (configurable retention)
- Sends Telegram notifications
"""
import gzip
import os
import subprocess
import sys
from datetime import datetime, timedelta, timezone

import boto3
import httpx
from botocore.config import Config as BotoConfig
from botocore.exceptions import ClientError

from config import config


def create_s3_client():
    """Initialize S3 client (same pattern as backend storage.py)."""
    return boto3.client(
        "s3",
        endpoint_url=config.S3_ENDPOINT_URL,
        aws_access_key_id=config.S3_ACCESS_KEY_ID,
        aws_secret_access_key=config.S3_SECRET_ACCESS_KEY,
        region_name=config.S3_REGION or "us-east-1",
        config=BotoConfig(signature_version="s3v4"),
    )


def send_telegram_notification(message: str, is_error: bool = False) -> None:
    """Send notification to Telegram admin."""
    if not config.TELEGRAM_BOT_TOKEN or not config.TELEGRAM_ADMIN_ID:
        print("Telegram not configured, skipping notification")
        return

    emoji = "\u274c" if is_error else "\u2705"
    text = f"{emoji} *Database Backup*\n\n{message}"

    url = f"https://api.telegram.org/bot{config.TELEGRAM_BOT_TOKEN}/sendMessage"
    data = {
        "chat_id": config.TELEGRAM_ADMIN_ID,
        "text": text,
        "parse_mode": "Markdown",
    }

    try:
        response = httpx.post(url, json=data, timeout=30)
        response.raise_for_status()
        print("Telegram notification sent")
    except Exception as e:
        print(f"Failed to send Telegram notification: {e}")


def create_backup() -> tuple[str, bytes]:
    """Create pg_dump backup and compress it."""
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    filename = f"marathon_backup_{timestamp}.sql.gz"

    # Build pg_dump command
    env = os.environ.copy()
    env["PGPASSWORD"] = config.DB_PASSWORD

    cmd = [
        "pg_dump",
        "-h",
        config.DB_HOST,
        "-p",
        config.DB_PORT,
        "-U",
        config.DB_USER,
        "-d",
        config.DB_NAME,
        "--no-owner",
        "--no-acl",
        "--clean",  # Add DROP commands before CREATE
        "--if-exists",  # Use IF EXISTS with DROP commands
        "-F",
        "p",  # plain SQL format
    ]

    print(f"Running pg_dump for database {config.DB_NAME}...")
    result = subprocess.run(
        cmd,
        env=env,
        capture_output=True,
    )

    if result.returncode != 0:
        raise Exception(f"pg_dump failed: {result.stderr.decode()}")

    # Compress the output
    print("Compressing backup...")
    compressed = gzip.compress(result.stdout, compresslevel=9)

    return filename, compressed


def upload_to_s3(s3_client, filename: str, data: bytes) -> str:
    """Upload backup to S3."""
    key = f"{config.S3_BACKUP_PREFIX}{filename}"

    print(f"Uploading to S3: {key}...")
    s3_client.put_object(
        Bucket=config.S3_BUCKET_NAME,
        Key=key,
        Body=data,
        ContentType="application/gzip",
    )

    return key


def rotate_old_backups(s3_client) -> int:
    """Delete backups older than BACKUP_RETENTION_DAYS."""
    cutoff_date = datetime.now(timezone.utc) - timedelta(
        days=config.BACKUP_RETENTION_DAYS
    )
    deleted_count = 0

    print(f"Rotating backups older than {config.BACKUP_RETENTION_DAYS} days...")

    # List all objects with backup prefix
    try:
        paginator = s3_client.get_paginator("list_objects_v2")
        pages = paginator.paginate(
            Bucket=config.S3_BUCKET_NAME,
            Prefix=config.S3_BACKUP_PREFIX,
        )

        for page in pages:
            for obj in page.get("Contents", []):
                last_modified = obj["LastModified"]
                if last_modified.tzinfo is None:
                    last_modified = last_modified.replace(tzinfo=timezone.utc)

                if last_modified < cutoff_date:
                    s3_client.delete_object(
                        Bucket=config.S3_BUCKET_NAME,
                        Key=obj["Key"],
                    )
                    deleted_count += 1
                    print(f"Deleted old backup: {obj['Key']}")
    except ClientError as e:
        print(f"Error during rotation: {e}")

    return deleted_count


def main() -> int:
    """Main backup routine."""
    start_time = datetime.now()

    print(f"{'=' * 50}")
    print(f"Backup started at {start_time}")
    print(f"{'=' * 50}")

    try:
        # Validate configuration
        if not config.S3_BUCKET_NAME:
            raise Exception("S3_BUCKET_NAME is not configured")
        if not config.S3_ACCESS_KEY_ID:
            raise Exception("S3_ACCESS_KEY_ID is not configured")
        if not config.S3_SECRET_ACCESS_KEY:
            raise Exception("S3_SECRET_ACCESS_KEY is not configured")
        if not config.S3_ENDPOINT_URL:
            raise Exception("S3_ENDPOINT_URL is not configured")

        # Create S3 client
        s3_client = create_s3_client()

        # Create backup
        filename, data = create_backup()
        size_mb = len(data) / (1024 * 1024)
        print(f"Backup created: {filename} ({size_mb:.2f} MB)")

        # Upload to S3
        s3_key = upload_to_s3(s3_client, filename, data)
        print(f"Uploaded to S3: {s3_key}")

        # Rotate old backups
        deleted_count = rotate_old_backups(s3_client)
        print(f"Deleted {deleted_count} old backups")

        # Calculate duration
        duration = datetime.now() - start_time

        # Send success notification
        message = (
            f"Backup completed successfully!\n\n"
            f"*File:* `{filename}`\n"
            f"*Size:* {size_mb:.2f} MB\n"
            f"*Duration:* {duration.seconds}s\n"
            f"*Deleted old:* {deleted_count} files"
        )
        send_telegram_notification(message, is_error=False)

        print(f"{'=' * 50}")
        print("Backup completed successfully!")
        print(f"{'=' * 50}")
        return 0

    except Exception as e:
        error_msg = f"Backup failed!\n\n*Error:* `{str(e)}`"
        send_telegram_notification(error_msg, is_error=True)
        print(f"{'=' * 50}")
        print(f"Backup failed: {e}")
        print(f"{'=' * 50}")
        return 1


if __name__ == "__main__":
    sys.exit(main())
