/**
 * POST /api/analyzer/itglue/configurations/:id/apply
 *
 * Same shape as the Applications apply route but PATCHes /configurations/:id
 * with flat attributes (no traits blob). All audit-trail layers identical.
 */

import { NextRequest, NextResponse } from 'next/server';
import { requirePermission } from '@/lib/auth-utils';
import postgresClient from '@/lib/services/postgres-client';
import { ApplyAssetSuggestionRequest } from '@/lib/types/analyzer';
import {
  createPendingWrite,
  getAssetAuditById,
  markWriteCommitted,
  markWriteFailed,
} from '@/lib/services/analyzer/asset-audit/persistence';
import { insertUpdatedXref } from '@/lib/services/analyzer/asset-audit/xrefs';
import { getITGlueClient } from '@/lib/services/itglue-client';
import { getITGlueSyncService } from '@/lib/services/itglue-sync-service';
import { audit } from '@/lib/services/audit';

interface ConfigRow {
  id: string;
  name: string;
  hostname: string | null;
  primary_ip: string | null;
  mac_address: string | null;
  serial_number: string | null;
  asset_tag: string | null;
  position: string | null;
  notes: string | null;
  operating_system_notes: string | null;
}

const CONFIG_EDITABLE_COLUMNS = [
  'name',
  'hostname',
  'primary_ip',
  'mac_address',
  'serial_number',
  'asset_tag',
  'position',
  'notes',
  'operating_system_notes',
] as const;

const FIELD_TO_ITGLUE_ATTR: Record<string, string> = {
  name: 'name',
  hostname: 'hostname',
  primary_ip: 'primary-ip',
  mac_address: 'mac-address',
  serial_number: 'serial-number',
  asset_tag: 'asset-tag',
  position: 'position',
  notes: 'notes',
  operating_system_notes: 'operating-system-notes',
};

async function loadConfigurationRow(assetId: string): Promise<ConfigRow | null> {
  const res = await postgresClient.query<ConfigRow>(
    `SELECT id::text AS id,
            name, hostname, primary_ip, mac_address, serial_number,
            asset_tag, position, notes, operating_system_notes
       FROM itg_configurations
      WHERE id = $1
      LIMIT 1`,
    [assetId]
  );
  return res.rowCount === 0 ? null : res.rows[0];
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { session, error } = await requirePermission('itglue', 'write');
  if (error) return error;

  const { id: assetId } = await params;
  const body = await request.json().catch(() => ({}));
  const parsed = ApplyAssetSuggestionRequest.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json(
      { error: 'Invalid request body', details: parsed.error.issues },
      { status: 400 }
    );
  }
  const { auditId, fieldName, suggestedValue, sourceEvidence } = parsed.data;

  const auditRow = await getAssetAuditById(auditId);
  if (!auditRow) {
    return NextResponse.json({ error: 'Audit not found' }, { status: 404 });
  }
  if (auditRow.asset_id !== assetId || auditRow.asset_type !== 'configuration') {
    return NextResponse.json(
      { error: 'Audit does not reference this configuration' },
      { status: 400 }
    );
  }

  const lowerField = fieldName.toLowerCase();
  if (/(password|secret|key|token|credential)/.test(lowerField)) {
    return NextResponse.json(
      { error: 'Refusing to write to credential-shaped field' },
      { status: 400 }
    );
  }

  // Refuse to write fields the audit pipeline shouldn't be touching on a
  // Configuration. We only let it edit the columns flagged editable.
  if (!CONFIG_EDITABLE_COLUMNS.includes(fieldName as (typeof CONFIG_EDITABLE_COLUMNS)[number])) {
    return NextResponse.json(
      {
        error: `Field '${fieldName}' is not editable via the audit pipeline`,
        editableFields: CONFIG_EDITABLE_COLUMNS,
      },
      { status: 400 }
    );
  }

  const config = await loadConfigurationRow(assetId);
  if (!config) {
    return NextResponse.json({ error: 'Configuration not found' }, { status: 404 });
  }
  const beforeValue =
    (config as unknown as Record<string, unknown>)[fieldName] ?? null;
  const userId =
    (session?.user as { id: string; email?: string } | undefined)?.id ?? null;
  const userEmail =
    (session?.user as { id: string; email?: string } | undefined)?.email ??
    undefined;

  const writeRow = await createPendingWrite({
    audit_id: auditId,
    asset_type: 'configuration',
    asset_id: assetId,
    field_name: fieldName,
    before_value: beforeValue,
    after_value: suggestedValue,
    performed_by_user_id: userId,
    source_evidence: sourceEvidence ?? null,
    triggered_by_ticket_number: auditRow.triggered_by_ticket_number ?? null,
  });

  // IT Glue PATCH attribute — convert snake to dash form.
  const attrKey = FIELD_TO_ITGLUE_ATTR[fieldName];
  const attributes = { [attrKey]: suggestedValue };

  try {
    const client = getITGlueClient();
    const updated = await client.updateConfiguration(assetId, attributes);
    await markWriteCommitted(writeRow.id, updated);

    try {
      await getITGlueSyncService().refreshConfigurationById(assetId);
    } catch (refreshErr) {
      console.warn(
        `[itglue-config-apply] mirror refresh failed for ${assetId}:`,
        refreshErr instanceof Error ? refreshErr.message : refreshErr
      );
    }

    await audit.log({
      userId: userId ?? undefined,
      userEmail,
      action: 'itglue.write',
      resource: 'configuration',
      resourceId: assetId,
      details: {
        write_id: writeRow.id,
        audit_id: auditId,
        field_name: fieldName,
        before: beforeValue,
        after: suggestedValue,
      },
    });

    if (auditRow.triggered_by_ticket_number) {
      try {
        await insertUpdatedXref({
          ticketNumber: auditRow.triggered_by_ticket_number,
          analysisId: auditRow.triggered_by_analysis_id ?? null,
          assetType: 'configuration',
          assetId,
          writeId: writeRow.id,
          fieldName,
        });
      } catch (xrefErr) {
        console.warn(
          `[itglue-config-apply] xref insert failed for write ${writeRow.id}:`,
          xrefErr instanceof Error ? xrefErr.message : xrefErr
        );
      }
    }

    return NextResponse.json({
      writeId: writeRow.id,
      status: 'committed',
      asset: updated,
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    await markWriteFailed(writeRow.id, message);
    return NextResponse.json(
      {
        writeId: writeRow.id,
        status: 'failed',
        error: 'IT Glue write failed',
        message,
      },
      { status: 502 }
    );
  }
}
