import { Router, Request, Response } from 'express';
import { param } from '../utils/params';
import fs from 'fs/promises';
import path from 'path';
import { exec as execCb } from 'child_process';
import { promisify } from 'util';
import * as docker from '../services/docker.service';
import { getSlugEntry } from '../services/registry.service';
import { env } from '../config/env';
import { logger } from '../utils/logger';

const exec = promisify(execCb);
const router = Router();

// POST /instance/:slug/backup — Run pg_dump + tar uploads → return backup info
router.post('/instance/:slug/backup', async (req: Request, res: Response) => {
  const entry = await getSlugEntry(param(req, 'slug'));
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const backupDir = path.join(env.AGENT_DATA_DIR, 'backups', param(req, 'slug'), timestamp);
  await fs.mkdir(backupDir, { recursive: true });

  const { pgPassword } = req.body;

  try {
    // 1. pg_dump
    const dumpFile = path.join(backupDir, 'database.sql');
    const dump = await docker.composeExec(
      entry.basePath, entry.composeProject,
      'v2-postgres',
      'pg_dump -U changemaker -d changemaker',
      300_000,
      pgPassword ? { PGPASSWORD: pgPassword } : undefined
    );
    await fs.writeFile(dumpFile, dump, 'utf-8');

    // Gzip the dump
    await exec(`gzip '${dumpFile}'`, { timeout: 120_000 });

    // 2. Tar uploads if exists
    const uploadsDir = path.join(entry.basePath, 'uploads');
    let hasUploads = false;
    try {
      await fs.access(uploadsDir);
      hasUploads = true;
    } catch { /* no uploads dir */ }

    if (hasUploads) {
      await exec(
        `tar -czf '${path.join(backupDir, 'uploads.tar.gz')}' -C '${entry.basePath}' uploads`,
        { timeout: 300_000 }
      );
    }

    // 3. Create final archive
    const archiveName = `backup-${param(req, 'slug')}-${timestamp}.tar.gz`;
    const archivePath = path.join(env.AGENT_DATA_DIR, 'backups', archiveName);
    await exec(
      `tar -czf '${archivePath}' -C '${path.dirname(backupDir)}' '${timestamp}'`,
      { timeout: 300_000 }
    );

    // Clean up temp dir
    await fs.rm(backupDir, { recursive: true, force: true });

    const stats = await fs.stat(archivePath);
    const backupId = timestamp;

    logger.info(`[backup] Created backup for ${param(req, 'slug')}: ${archivePath} (${stats.size} bytes)`);

    res.json({
      backupId,
      archivePath,
      sizeBytes: stats.size,
      timestamp,
    });
  } catch (err) {
    // Clean up on failure
    try { await fs.rm(backupDir, { recursive: true, force: true }); } catch { /* ignore */ }
    throw err;
  }
});

// GET /instance/:slug/backup/:id/download — Stream backup archive
router.get('/instance/:slug/backup/:id/download', async (req: Request, res: Response) => {
  const archiveName = `backup-${param(req, 'slug')}-${param(req, 'id')}.tar.gz`;
  const archivePath = path.join(env.AGENT_DATA_DIR, 'backups', archiveName);

  try {
    await fs.access(archivePath);
  } catch {
    res.status(404).json({ error: 'NOT_FOUND', message: 'Backup archive not found' });
    return;
  }

  const stats = await fs.stat(archivePath);
  res.setHeader('Content-Type', 'application/gzip');
  res.setHeader('Content-Length', stats.size);
  res.setHeader('Content-Disposition', `attachment; filename="${archiveName}"`);

  const { createReadStream } = await import('fs');
  const stream = createReadStream(archivePath);
  stream.pipe(res);
});

export default router;
