/**
 * PATCH  /api/route53/zones/:zoneId/records/:recordId — update a resource
 *   record set (Route 53 UPSERT).
 * DELETE /api/route53/zones/:zoneId/records/:recordId — delete a resource
 *   record set.
 *
 * `recordId` is the URL-encoded `record_key`
 * (`${zoneId}:${name}:${type}:${setIdentifier ?? ''}`).
 *
 * Both handlers follow the same eight-step sequence as POST in
 * ../route.ts:
 *  1. requireAdmin() (D-04) — never rely on the UI hiding a control (T-24-02)
 *  2. isRoute53Configured() -> 503
 *  3. parse params + body
 *  4. validateRecordWrite() (D-01, before any AWS command is constructed)
 *  5. loadMirrorRecord() for beforeValue; null -> 404. The loaded row's exact
 *     name/type/ttl/resourceRecords are what get submitted to AWS — Route 53
 *     rejects or mis-targets a DELETE whose recordset doesn't match exactly
 *     (24-RESEARCH.md Pitfall 3). Never build a DELETE from client-supplied
 *     { name, type } alone.
 *  6. createPendingAuditLog before any AWS call (D-07/SC-3)
 *  7. submitRecordChange + pollChangeStatus -> markAuditCommitted ->
 *     insertPulseCrudHistory -> upsertMirrorRecord/softDeleteMirrorRecord -> 200
 *  8. catch -> sanitizeAwsError -> markAuditFailed -> 502 (no history row —
 *     nothing changed on AWS's side, 24-RESEARCH.md Pattern 3)
 *
 * D-03 compliance: both mutations execute on the first request. No `confirm`
 * body flag, no two-phase endpoint, no staged-approval status column — the
 * audit trail is the control, not a pre-write block.
 */

import { NextRequest, NextResponse } from 'next/server';
import { requireAdmin } from '@/lib/auth-utils';
import { isRoute53Configured } from '@/lib/services/route53-factory';
import { validateRecordWrite, sanitizeAwsError } from '@/lib/services/route53-record-validation';
import {
  createPendingAuditLog,
  markAuditCommitted,
  markAuditFailed,
  insertPulseCrudHistory,
  upsertMirrorRecord,
  softDeleteMirrorRecord,
  loadMirrorRecord,
} from '@/lib/services/route53-write-persistence';
import { submitRecordChange, pollChangeStatus } from '@/lib/services/route53-change-submit';

/**
 * Verify the decoded record key's zone prefix matches the `zoneId` path
 * param — prevents a caller from mutating a record in a different zone
 * through a mismatched path (T-24-17).
 */
function recordKeyMatchesZone(recordKey: string, zoneId: string): boolean {
  return recordKey.startsWith(`${zoneId}:`);
}

export async function PATCH(
  request: NextRequest,
  { params }: { params: Promise<{ zoneId: string; recordId: string }> }
) {
  const { session, error } = await requireAdmin();
  if (error) return error;

  if (!isRoute53Configured()) {
    return NextResponse.json(
      {
        error: 'Route 53 not configured',
        message: 'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set to write Route 53 records',
      },
      { status: 503 }
    );
  }

  const { zoneId, recordId } = await params;
  const recordKey = decodeURIComponent(recordId);
  if (!recordKeyMatchesZone(recordKey, zoneId)) {
    return NextResponse.json({ error: 'Record does not belong to this zone' }, { status: 400 });
  }

  const body = await request.json().catch(() => ({}));
  const validated = validateRecordWrite(body);
  if (!validated.ok) {
    return NextResponse.json({ error: 'Invalid record', message: validated.reason }, { status: 400 });
  }
  const { name, type, ttl, resourceRecords } = validated.value;
  const setIdentifier: string | null =
    typeof body.setIdentifier === 'string' && body.setIdentifier.trim().length > 0
      ? body.setIdentifier.trim()
      : null;

  const existing = await loadMirrorRecord(recordKey);
  if (!existing) {
    return NextResponse.json({ error: 'Record not found' }, { status: 404 });
  }
  const beforeValue = {
    name: existing.name,
    type: existing.type,
    ttl: existing.ttl,
    setIdentifier: existing.setIdentifier,
    resourceRecords: existing.resourceRecords,
  };
  const afterValue = { name, type, ttl, setIdentifier, resourceRecords };

  const audit = await createPendingAuditLog({
    operation: 'update',
    zoneId,
    recordKey,
    recordName: name,
    recordType: type,
    beforeValue,
    afterValue,
    performedByUserId: session!.user.id,
    performedByEmail: session!.user.email,
  });

  try {
    const { changeId, awsResponse } = await submitRecordChange({
      zoneId,
      action: 'UPSERT',
      recordSet: { name, type, ttl, resourceRecords, setIdentifier },
    });
    const propagationStatus = changeId ? await pollChangeStatus(changeId) : 'PENDING';

    await markAuditCommitted(audit.id, changeId, propagationStatus, awsResponse);
    await insertPulseCrudHistory({
      zoneId,
      recordKey,
      recordName: name,
      recordType: type,
      changeAction: 'update',
      beforeValue,
      afterValue,
      changedByUserId: session!.user.id,
      changedByEmail: session!.user.email,
      auditLogId: audit.id,
    });
    await upsertMirrorRecord({
      recordKey,
      zoneId,
      name,
      type,
      setIdentifier,
      ttl,
      resourceRecords,
      aliasTarget: null,
      rawPayload: afterValue,
    });

    return NextResponse.json({
      auditId: audit.id,
      status: 'committed',
      propagationStatus,
      record: afterValue,
    });
  } catch (err) {
    const message = sanitizeAwsError(err);
    console.error('[ROUTE53-WRITE] update failed:', message);
    await markAuditFailed(audit.id, err);
    return NextResponse.json(
      { auditId: audit.id, status: 'failed', error: 'Route 53 write failed', message },
      { status: 502 }
    );
  }
}

export async function DELETE(
  _request: NextRequest,
  { params }: { params: Promise<{ zoneId: string; recordId: string }> }
) {
  const { session, error } = await requireAdmin();
  if (error) return error;

  if (!isRoute53Configured()) {
    return NextResponse.json(
      {
        error: 'Route 53 not configured',
        message: 'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set to write Route 53 records',
      },
      { status: 503 }
    );
  }

  const { zoneId, recordId } = await params;
  const recordKey = decodeURIComponent(recordId);
  if (!recordKeyMatchesZone(recordKey, zoneId)) {
    return NextResponse.json({ error: 'Record does not belong to this zone' }, { status: 400 });
  }

  const existing = await loadMirrorRecord(recordKey);
  if (!existing) {
    return NextResponse.json({ error: 'Record not found' }, { status: 404 });
  }

  // D-01 defence in depth: validate the type of the record being deleted the
  // same way a create/update is validated — an NS/SOA delete is as
  // destructive as an NS/SOA write.
  const validated = validateRecordWrite({
    name: existing.name,
    type: existing.type,
    ttl: existing.ttl ?? undefined,
    resourceRecords: existing.resourceRecords ?? [],
  });
  if (!validated.ok) {
    return NextResponse.json({ error: 'Invalid record', message: validated.reason }, { status: 400 });
  }

  // The exact current recordset read from the mirror — Route 53 rejects or
  // mis-targets a DELETE that does not match exactly (24-RESEARCH.md
  // Pitfall 3). Never build this from client-supplied values.
  const { name, type, ttl, resourceRecords } = validated.value;
  const setIdentifier = existing.setIdentifier;
  const beforeValue = { name, type, ttl, setIdentifier, resourceRecords };

  const audit = await createPendingAuditLog({
    operation: 'delete',
    zoneId,
    recordKey,
    recordName: name,
    recordType: type,
    beforeValue,
    afterValue: null,
    performedByUserId: session!.user.id,
    performedByEmail: session!.user.email,
  });

  try {
    const { changeId, awsResponse } = await submitRecordChange({
      zoneId,
      action: 'DELETE',
      recordSet: { name, type, ttl, resourceRecords, setIdentifier: existing.setIdentifier },
    });
    const propagationStatus = changeId ? await pollChangeStatus(changeId) : 'PENDING';

    await markAuditCommitted(audit.id, changeId, propagationStatus, awsResponse);
    await insertPulseCrudHistory({
      zoneId,
      recordKey,
      recordName: name,
      recordType: type,
      changeAction: 'delete',
      beforeValue,
      afterValue: null,
      changedByUserId: session!.user.id,
      changedByEmail: session!.user.email,
      auditLogId: audit.id,
    });
    await softDeleteMirrorRecord(recordKey);

    return NextResponse.json({
      auditId: audit.id,
      status: 'committed',
      propagationStatus,
      record: null,
    });
  } catch (err) {
    const message = sanitizeAwsError(err);
    console.error('[ROUTE53-WRITE] delete failed:', message);
    await markAuditFailed(audit.id, err);
    return NextResponse.json(
      { auditId: audit.id, status: 'failed', error: 'Route 53 write failed', message },
      { status: 502 }
    );
  }
}
