/**
 * GET / PUT / DELETE /api/telephone/jambonz/credentials
 *
 * Restaurant-scoped jambonz API credentials (provider_name='jambonz' in
 * `restaurant_api_keys`). Falls back to platform env vars when no row
 * exists. Gated by FEATURE_SIP_ENABLED + auth so the surface only exists
 * for tenants who actually have the SIP add-on.
 *
 * GET returns a redacted view (key hint + presence of env fallback).
 * PUT upserts the API key + base_url + account_sid (encrypted at rest).
 * DELETE removes the row, reverting the tenant to env-only fallback.
 */
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, requireSection, AuthedRequest } from '@server/middleware/withAuth';
import { withSipFeatureAuthed } from '@server/middleware/withSipFeature';
import { withValidationAuthed } from '@server/middleware/withValidation';
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { encrypt } from '@server/utils/crypto';

function makeKeyHint(key: string): string {
  if (!key) return '';
  if (key.length <= 8) return '****';
  return key.slice(0, 6) + '...' + key.slice(-4);
}

export const GET = withErrorHandler(
  withAuth(
    withSipFeatureAuthed(async (req: AuthedRequest) => {
      await requireSection(req, 'telephone');
      await initDatabase();
      const restaurantId = req.session.restaurantId!;
      /* raw: SELECT key_hint, is_active, metadata, updated_at FROM restaurant_api_keys WHERE restaurant_id = $1 AND provider_name='jambonz' LIMIT 1 */
      const { rows } = await db.execute(sql`
        SELECT key_hint, is_active, metadata, updated_at
        FROM restaurant_api_keys
        WHERE restaurant_id = ${restaurantId} AND provider_name = 'jambonz' LIMIT 1
      `);
      const row = rows[0] as { key_hint: string; is_active: boolean; metadata: Record<string, unknown> | null; updated_at: string } | undefined;
      const meta = (row?.metadata || {}) as { base_url?: string; account_sid?: string; encrypted_webhook_secret?: string; webhook_secret_hint?: string };
      return NextResponse.json({
        configured: !!row,
        key_hint: row?.key_hint || '',
        base_url: meta.base_url || '',
        account_sid: meta.account_sid || '',
        webhook_secret_configured: !!meta.encrypted_webhook_secret,
        webhook_secret_hint: meta.webhook_secret_hint || '',
        is_active: !!row?.is_active,
        updated_at: row?.updated_at || null,
        env_fallback: {
          base_url: !!process.env.JAMBONZ_BASE_URL,
          api_key: !!process.env.JAMBONZ_API_KEY,
          account_sid: !!process.env.JAMBONZ_ACCOUNT_SID,
          webhook_secret: !!process.env.JAMBONZ_WEBHOOK_SECRET,
        },
      });
    })
  )
);

// `api_key` is optional on PUT so the operator can update base_url /
// account_sid without re-entering the secret (matches the UI hint
// "leave blank to keep saved key"). When omitted, the existing
// encrypted_api_key + key_hint are preserved; metadata is overwritten.
const putSchema = z.object({
  api_key: z.string().min(8).optional(),
  base_url: z.string().url('Base URL must be a valid URL'),
  account_sid: z.string().min(1, 'Account SID is required'),
  // Per-restaurant HMAC secret used to verify jambonz webhooks. Optional;
  // when omitted, the existing saved value (if any) is preserved and the
  // platform env JAMBONZ_WEBHOOK_SECRET is used as fallback at verify-time.
  webhook_secret: z.string().min(8).optional(),
});

export const PUT = withErrorHandler(
  withAuth(
    withSipFeatureAuthed(
      withValidationAuthed(putSchema, async (req) => {
        await requireSection(req, 'telephone', 'update');
        await initDatabase();
        const restaurantId = req.session.restaurantId!;
        const { api_key, base_url, account_sid, webhook_secret } = req.parsedBody as z.infer<typeof putSchema>;

        // Pull the existing row so we can preserve api_key and/or webhook
        // secret when the operator only updates one field at a time.
        const { rows: existingRows } = await db.execute(sql`
          SELECT encrypted_api_key, key_hint, metadata FROM restaurant_api_keys
          WHERE restaurant_id = ${restaurantId} AND provider_name = 'jambonz' LIMIT 1
        `);
        const existing = existingRows[0] as { encrypted_api_key: string; key_hint: string; metadata: Record<string, unknown> | null } | undefined;
        const existingMeta = (existing?.metadata || {}) as { encrypted_webhook_secret?: string; webhook_secret_hint?: string };

        if (!existing && !api_key) {
          return NextResponse.json(
            { error: 'API key is required when no jambonz credentials are saved yet.' },
            { status: 400 }
          );
        }

        const encryptedKey = api_key && api_key.trim()
          ? encrypt(api_key.trim())
          : existing!.encrypted_api_key;
        const keyHint = api_key && api_key.trim()
          ? makeKeyHint(api_key.trim())
          : existing!.key_hint;

        const encryptedSecret = webhook_secret && webhook_secret.trim()
          ? encrypt(webhook_secret.trim())
          : existingMeta.encrypted_webhook_secret;
        const secretHint = webhook_secret && webhook_secret.trim()
          ? makeKeyHint(webhook_secret.trim())
          : existingMeta.webhook_secret_hint;

        // Merge into the existing metadata so unrelated fields added in the
        // future aren't dropped when the operator only updates a subset.
        const mergedMeta: Record<string, unknown> = {
          ...(existing?.metadata || {}),
          base_url: base_url.replace(/\/+$/, ''),
          account_sid: account_sid.trim(),
        };
        if (encryptedSecret) {
          mergedMeta.encrypted_webhook_secret = encryptedSecret;
          mergedMeta.webhook_secret_hint = secretHint;
        }
        const meta = JSON.stringify(mergedMeta);

        /* raw: INSERT ... ON CONFLICT (restaurant_id, provider_name) DO UPDATE ... */
        await db.execute(sql`
          INSERT INTO restaurant_api_keys (restaurant_id, provider_name, encrypted_api_key, key_hint, metadata, is_active)
          VALUES (${restaurantId}, 'jambonz', ${encryptedKey}, ${keyHint}, ${meta}::jsonb, true)
          ON CONFLICT (restaurant_id, provider_name)
          DO UPDATE SET encrypted_api_key = ${encryptedKey}, key_hint = ${keyHint}, metadata = ${meta}::jsonb, is_active = true, updated_at = NOW()
        `);
        return NextResponse.json({ success: true, key_hint: keyHint });
      })
    )
  )
);

export const DELETE = withErrorHandler(
  withAuth(
    withSipFeatureAuthed(async (req: AuthedRequest) => {
      await requireSection(req, 'telephone', 'delete');
      await initDatabase();
      const restaurantId = req.session.restaurantId!;
      await db.execute(sql`DELETE FROM restaurant_api_keys WHERE restaurant_id = ${restaurantId} AND provider_name = 'jambonz'`);
      return NextResponse.json({ success: true });
    })
  )
);
