import { NextResponse } from 'next/server';
import { sql } from 'drizzle-orm';

import { db } from '@/lib/db';
import { redis } from '@/lib/redis';
import { getStorageBackend } from '@/lib/storage';

type CheckStatus = 'ok' | 'error';

interface ReadyChecks {
  postgres: CheckStatus;
  redis: CheckStatus;
  storage: CheckStatus;
}

interface ReadyResponse {
  status: 'ready' | 'degraded';
  checks: ReadyChecks;
  timestamp: string;
}

/**
 * Readiness probe - verifies that every backing service this process
 * needs to serve traffic is reachable. A 503 should drop the pod from the
 * load balancer until the next probe succeeds; it should not trigger a
 * pod restart (that's what `/api/health` is for).
 *
 * Checks:
 *   - postgres: `SELECT 1` against the primary
 *   - redis:    `PING`
 *   - storage:  HEAD on a sentinel key via the active storage backend
 *               (S3 or filesystem). Health-checks whichever backend
 *               this port is configured for, not always MinIO.
 *
 * Documenso + SMTP are intentionally not probed here: they're optional
 * integrations, and each tenant configures its own credentials. A
 * tenant-misconfigured Documenso instance shouldn't deadline the entire
 * shared CRM.
 */
export async function GET(): Promise<NextResponse<ReadyResponse>> {
  const checks: ReadyChecks = {
    postgres: 'error',
    redis: 'error',
    storage: 'error',
  };

  await Promise.allSettled([
    db
      .execute(sql`SELECT 1`)
      .then(() => {
        checks.postgres = 'ok';
      })
      .catch(() => {
        checks.postgres = 'error';
      }),

    redis
      .ping()
      .then(() => {
        checks.redis = 'ok';
      })
      .catch(() => {
        checks.redis = 'error';
      }),

    (async () => {
      try {
        const backend = await getStorageBackend();
        // head() returns null for a missing object (both backends);
        // the connection itself succeeding is what counts.
        await backend.head('__health_probe__');
        checks.storage = 'ok';
      } catch {
        checks.storage = 'error';
      }
    })(),
  ]);

  const allReady = Object.values(checks).every((s) => s === 'ok');
  const status: ReadyResponse['status'] = allReady ? 'ready' : 'degraded';

  return NextResponse.json(
    { status, checks, timestamp: new Date().toISOString() },
    { status: allReady ? 200 : 503 },
  );
}
