import { NextResponse } from "next/server";
import { exec } from "child_process";
import { promisify } from "util";
import { S3Client, PutObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
import fs from "fs/promises";
import { gzip } from "zlib";
import { sendAlert } from "@/lib/alert";

// Below this, a gzipped dump almost certainly means an empty/failed pg_dump.
const MIN_BACKUP_BYTES = 1024;

const execAsync = promisify(exec);
const gzipAsync = promisify(gzip);

export async function POST(request: Request) {
  const secret = request.headers.get("x-cron-secret");
  if (secret !== process.env.CRON_SECRET) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const date = new Date().toISOString().split("T")[0];
  const filename = `tia-backup-${date}.sql.gz`;
  const tmpPath = `/tmp/${filename}`;

  try {
    // 1. Dump database
    const dbUrl = process.env.DATABASE_URL_SUPERUSER || process.env.DATABASE_URL;
    await execAsync(`pg_dump "${dbUrl}" -F p -f /tmp/dump.sql`);

    // 2. Compress
    const raw = await fs.readFile("/tmp/dump.sql");
    const compressed = await gzipAsync(raw);

    // 3. Upload to R2
    if (process.env.R2_BACKUP_BUCKET && process.env.R2_BACKUP_ACCESS_KEY_ID) {
      const s3 = new S3Client({
        region: "auto",
        endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
        credentials: {
          accessKeyId: process.env.R2_BACKUP_ACCESS_KEY_ID!,
          secretAccessKey: process.env.R2_BACKUP_SECRET_ACCESS_KEY!,
        },
      });

      await s3.send(new PutObjectCommand({
        Bucket: process.env.R2_BACKUP_BUCKET!,
        Key: filename,
        Body: compressed,
      }));

      // 4. Delete backups older than 30 days
      const list = await s3.send(new ListObjectsV2Command({
        Bucket: process.env.R2_BACKUP_BUCKET!,
      }));

      const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
      for (const obj of list.Contents || []) {
        if (obj.LastModified && obj.LastModified.getTime() < cutoff) {
          await s3.send(new DeleteObjectCommand({
            Bucket: process.env.R2_BACKUP_BUCKET!,
            Key: obj.Key!,
          }));
        }
      }
    }

    // 5. Cleanup local
    await fs.unlink("/tmp/dump.sql").catch(() => {});

    const sizeKb = Math.round(compressed.length / 1024);

    // Suspiciously small dump — almost certainly an empty or broken backup.
    if (compressed.length < MIN_BACKUP_BYTES) {
      await sendAlert("error", "Backup looks empty", `Compressed dump is only ${compressed.length} bytes`, {
        fields: { File: filename },
      });
    } else {
      await sendAlert("info", "Backup completed", undefined, {
        fields: { File: filename, Size: `${sizeKb} KB` },
        silent: true, // daily confirmation — no need to buzz
      });
    }

    return NextResponse.json({ success: true, filename, size: compressed.length });
  } catch (e) {
    console.error("Backup failed:", e);
    await sendAlert("fatal", "Database backup FAILED", String(e).slice(0, 500), {
      fields: { File: filename },
    });
    return NextResponse.json({ error: String(e) }, { status: 500 });
  }
}