/**
 * /api/public/website-inquiries route - unit tests.
 *
 * Asserts:
 *   1. Auth: rejects missing/wrong X-Webhook-Secret with 401.
 *   2. Validation: rejects malformed payloads (bad UUID, unknown kind,
 *      unknown port) with 400.
 *   3. Idempotency: a repeat submission_id returns the existing row id
 *      instead of inserting a duplicate.
 *
 * Uses HOISTED `vi.mock` (top of file) so mock state is established
 * before any module loads. Earlier attempts with `vi.doMock` inside
 * beforeEach proved flaky under parallel test-file execution because
 * other files' mocks leaked into ours via the shared module cache.
 *
 * The 503 path (WEBSITE_INTAKE_SECRET unset) is verified by manual
 * code inspection rather than a unit test - exercising it would
 * require runtime env mutation that conflicts with the hoisted mock.
 */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const VALID_UUID = '11111111-1111-4111-8111-111111111111';
const SECRET = 'test-secret-at-least-16-chars-long';

// ─── Mock state - module-scoped so test-level mutations are visible ────

interface MockState {
  portRow: Array<{ id: string }>;
  existingRow: Array<{ id: string }>;
  inserted: Array<Record<string, unknown>>;
  rateLimitAllowed: boolean;
  /** Counts select(...).limit(...) calls made by the route within a test.
   *  Call #1 = port lookup, call #2 = existing-submission lookup. Reset
   *  in beforeEach so each test starts fresh. */
  queryCount: number;
}

const state: MockState = {
  portRow: [{ id: 'port-uuid-port-nimara' }],
  existingRow: [],
  inserted: [],
  rateLimitAllowed: true,
  queryCount: 0,
};

// ─── Hoisted mocks - apply for the entire file ────────────────────────

vi.mock('@/lib/env', () => ({
  env: { WEBSITE_INTAKE_SECRET: SECRET },
}));

vi.mock('@/lib/rate-limit', () => ({
  rateLimiters: { publicForm: { limit: 10, window: 60_000 } },
  checkRateLimit: vi.fn(async () => ({
    allowed: state.rateLimitAllowed,
    resetAt: Date.now() + 60_000,
  })),
}));

vi.mock('@/lib/logger', () => ({
  logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));

vi.mock('@/lib/db', () => {
  const selectChain = {
    from: () => selectChain,
    where: () => selectChain,
    limit: async () => {
      // First select call in a test = port lookup; second = existing
      // submission lookup (only reached on conflict path now). Counter
      // is reset by beforeEach.
      state.queryCount += 1;
      return state.queryCount === 1 ? state.portRow : state.existingRow;
    },
  };
  // Insert chain mirrors Drizzle's `insert(...).values(...).onConflictDoNothing(...).returning()`.
  // When `state.existingRow` is non-empty (simulated existing row), the
  // returning() resolves to []; the route then falls back to the SELECT
  // for the existing row id. Otherwise returning() yields the new row.
  const insertChain = {
    values: (vals: Record<string, unknown>) => {
      const onConflictChain = {
        returning: async () => {
          if (state.existingRow.length > 0) {
            return []; // simulate conflict (no row inserted)
          }
          state.inserted.push(vals);
          return [{ id: 'generated-row-id' }];
        },
      };
      return {
        onConflictDoNothing: () => onConflictChain,
        // Backwards-compat: tests that don't go through the conflict
        // path can still call .returning() directly. Same semantics.
        returning: async () => {
          state.inserted.push(vals);
          return [{ id: 'generated-row-id' }];
        },
      };
    },
  };
  return {
    db: {
      select: () => selectChain,
      insert: () => insertChain,
    },
  };
});

// ─── Helpers ──────────────────────────────────────────────────────────

function makeReq(body: unknown, headers: Record<string, string> = {}) {
  return {
    headers: {
      get(name: string) {
        return headers[name.toLowerCase()] ?? null;
      },
    },
    json: async () => body,
  } as unknown as import('next/server').NextRequest;
}

beforeEach(() => {
  state.portRow = [{ id: 'port-uuid-port-nimara' }];
  state.existingRow = [];
  state.inserted = [];
  state.rateLimitAllowed = true;
  state.queryCount = 0;
});

afterEach(() => {
  vi.clearAllMocks();
});

// ─── Tests ────────────────────────────────────────────────────────────

describe('POST /api/public/website-inquiries - auth + capture', () => {
  it('returns 401 when the X-Webhook-Secret header is missing', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(makeReq({}));
    expect(res.status).toBe(401);
  });

  it('returns 401 when the X-Webhook-Secret header is wrong', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(makeReq({}, { 'x-webhook-secret': 'wrong-value-but-same-length-aaaa' }));
    expect(res.status).toBe(401);
  });

  it('returns 400 when the body fails validation', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        { submission_id: 'not-a-uuid', kind: 'berth_inquiry', payload: {} },
        {
          'x-webhook-secret': SECRET,
        },
      ),
    );
    expect(res.status).toBe(400);
  });

  it('returns 400 when port_slug references a non-existent port', async () => {
    state.portRow = [];
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'berth_inquiry',
          payload: { foo: 'bar' },
          port_slug: 'no-such-port',
        },
        { 'x-webhook-secret': SECRET },
      ),
    );
    expect(res.status).toBe(400);
    const body = (await res.json()) as any;
    expect(body.error).toMatch(/Unknown port/);
  });

  it('captures a fresh submission and returns its id', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'berth_inquiry',
          payload: { firstName: 'Jane', email: 'jane@example.com' },
          legacy_nocodb_id: '12345',
        },
        { 'x-webhook-secret': SECRET, 'user-agent': 'TestAgent/1.0' },
      ),
    );
    expect(res.status).toBe(200);
    const body = (await res.json()) as any;
    expect(body).toEqual({ id: 'generated-row-id', deduped: false });

    expect(state.inserted).toHaveLength(1);
    const row = state.inserted[0]!;
    expect(row.submissionId).toBe(VALID_UUID);
    expect(row.kind).toBe('berth_inquiry');
    expect(row.legacyNocodbId).toBe('12345');
    expect(row.userAgent).toBe('TestAgent/1.0');
    expect(row.payload).toEqual({ firstName: 'Jane', email: 'jane@example.com' });
  });

  it('returns 200 with deduped=true when the submission_id is already on file', async () => {
    state.existingRow = [{ id: 'existing-row-id' }];
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'contact_form',
          payload: {},
        },
        { 'x-webhook-secret': SECRET },
      ),
    );
    expect(res.status).toBe(200);
    const body = (await res.json()) as any;
    expect(body).toEqual({ id: 'existing-row-id', deduped: true });
    expect(state.inserted).toHaveLength(0);
  });

  it('defaults port_slug to port-nimara when omitted', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'residence_inquiry',
          payload: { foo: 'bar' },
        },
        { 'x-webhook-secret': SECRET },
      ),
    );
    expect(res.status).toBe(200);
    expect(state.inserted).toHaveLength(1);
    expect(state.inserted[0]!.portId).toBe('port-uuid-port-nimara');
  });

  it('rejects unknown kinds', async () => {
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'newsletter_signup',
          payload: {},
        },
        { 'x-webhook-secret': SECRET },
      ),
    );
    expect(res.status).toBe(400);
  });

  it('returns 429 when the rate limiter trips', async () => {
    state.rateLimitAllowed = false;
    const { POST } = await import('@/app/api/public/website-inquiries/route');
    const res = await POST(
      makeReq(
        {
          submission_id: VALID_UUID,
          kind: 'berth_inquiry',
          payload: {},
        },
        { 'x-webhook-secret': SECRET },
      ),
    );
    expect(res.status).toBe(429);
  });
});
