import { NextRequest, NextResponse } from "next/server";
import { requireFamily } from "@/lib/auth";
import { sql } from "@/db";
import { generateThumbnail } from "@/lib/media/thumbnail";
import { processMemoryVision } from "@/lib/ai/vision";
import { S3Client, HeadObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { reconcileActualSize } from "@/lib/quota";

function makeR2Client() {
  const accountId = process.env.R2_ACCOUNT_ID!;
  const accessKeyId = process.env.R2_ACCESS_KEY_ID!;
  const secretAccessKey = process.env.R2_SECRET_ACCESS_KEY!;
  return new S3Client({
    region: "auto",
    endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
    credentials: { accessKeyId, secretAccessKey },
  });
}

// POST /api/memories/[id]/confirm — called after client finishes uploading to R2
export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const auth = await requireFamily();
  if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status });

  const familyId = auth.session!.familyId!;

  const rows = await sql`
    SELECT id, r2_key, processing_status, size_bytes
    FROM memories
    WHERE id = ${id} AND family_id = ${familyId}
    LIMIT 1
  `;
  if (!rows[0]) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const { r2_key: r2Key, size_bytes: declaredBytes } = rows[0];

  // Fetch actual object size from R2 and reconcile against quota.
  // This catches cases where the client lied about the file size.
  let actualBytes: number | null = null;
  try {
    const client = makeR2Client();
    const head = await client.send(
      new HeadObjectCommand({ Bucket: process.env.R2_BUCKET_NAME!, Key: r2Key })
    );
    actualBytes = head.ContentLength ?? null;
  } catch {
    // If HeadObject fails the file didn't land — mark failed and return
    await sql`UPDATE memories SET processing_status = 'failed', updated_at = now() WHERE id = ${id}`;
    return NextResponse.json({ error: "File not found in storage after upload" }, { status: 422 });
  }

  // Reconcile actual size: updates size_bytes in DB and checks if family is over quota.
  // reconcileActualSize() always writes the real byte count regardless of plan.
  if (actualBytes !== null) {
    const reconcile = await reconcileActualSize(familyId, id, actualBytes);
    if (!reconcile.keep) {
      // Actual size pushed family over quota — delete the object and fail the upload.
      try {
        const client = makeR2Client();
        await client.send(
          new DeleteObjectCommand({ Bucket: process.env.R2_BUCKET_NAME!, Key: r2Key })
        );
      } catch {
        // Best-effort cleanup; log but don't block the error response.
        console.error(`[quota] failed to delete over-quota object key=${r2Key}`);
      }
      await sql`UPDATE memories SET processing_status = 'failed', updated_at = now() WHERE id = ${id}`;
      return NextResponse.json(
        { error: reconcile.reason, reason: "storage_quota_exceeded" },
        { status: 402 }
      );
    }
  }

  // Material size discrepancy check (client declared much less than actual).
  // 20 % tolerance — flag in logs but don't block if within quota.
  if (actualBytes !== null && declaredBytes !== null) {
    const discrepancy = actualBytes - Number(declaredBytes);
    if (discrepancy > Number(declaredBytes) * 0.2) {
      console.warn(
        `[quota] size discrepancy for memory ${id}: declared=${declaredBytes} actual=${actualBytes} delta=${discrepancy}`
      );
    }
  }

  // Mark as processing and start the media pipeline.
  // thumbnail failure is non-fatal — always proceed to vision, which marks 'ready'.
  await sql`UPDATE memories SET processing_status = 'processing', updated_at = now() WHERE id = ${id}`;

  generateThumbnail(id)
    .catch(e => console.error(`[thumbnail] id=${id}`, e))   // swallow so vision always runs
    .then(() => processMemoryVision(id))
    .catch(e => console.error(`[memory pipeline] id=${id}`, e));

  return NextResponse.json({ success: true, message: "Processing started", actualBytes });
}
