/**
 * GET /api/me — regression tests for dev-auth bypass path.
 *
 * Covers:
 *   1. DEV_AUTH_BYPASS=true (non-production): GET /api/me returns 200 with the injected
 *      DEV_USER identity (id=1, displayName='Dev User', color='#4A90D9').
 *      The OIDC guard must NOT be enforced — no Authelia env vars needed.
 *   2. Without DEV_AUTH_BYPASS: the OIDC middleware is still wired on /api/*.
 *      Verified structurally by asserting oidcAuthMiddleware is called during app init
 *      (the mock intercepts it and acts as a passthrough, confirming the mount path).
 *
 * Architecture note:
 *   devAuthBypass() and devBypassActive in index.ts both evaluate env vars at module
 *   load time. Tests must set process.env BEFORE importing the app module.
 *   vitest.resetModules() ensures each test gets a fresh module registry.
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// ---------------------------------------------------------------------------
// Shared mock: DB — avoids real DB connections across all tests in this file.
// This mock is hoisted by Vitest and applies to every dynamic import below.
// ---------------------------------------------------------------------------
vi.mock('../../src/db/client.js', () => ({
  db: {
    execute: vi.fn().mockResolvedValue([[{ '1': 1 }]]),
    select: vi.fn().mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockResolvedValue([]),
        innerJoin: vi.fn().mockReturnValue({
          innerJoin: vi.fn().mockReturnValue({
            where: vi.fn().mockResolvedValue([]),
          }),
        }),
      }),
    }),
  },
}));

// ---------------------------------------------------------------------------
// Track whether oidcAuthMiddleware was registered on the app.
// The spy is set up fresh per test via beforeEach/afterEach.
// ---------------------------------------------------------------------------
const oidcMiddlewareSpy = vi.fn(() => async (_c: unknown, next: () => Promise<void>) => next());

vi.mock('@hono/oidc-auth', () => ({
  oidcAuthMiddleware: () => oidcMiddlewareSpy(),
  processOAuthCallback: () => async (c: { json: (v: unknown) => unknown }) => c.json({ ok: true }),
  getAuth: vi.fn().mockResolvedValue(null),
}));

// ---------------------------------------------------------------------------
// Env snapshot — restored after each test to avoid cross-test pollution.
// ---------------------------------------------------------------------------
const originalNodeEnv = process.env.NODE_ENV;
const originalBypassFlag = process.env.DEV_AUTH_BYPASS;

afterEach(() => {
  process.env.NODE_ENV = originalNodeEnv;
  if (originalBypassFlag === undefined) {
    delete process.env.DEV_AUTH_BYPASS;
  } else {
    process.env.DEV_AUTH_BYPASS = originalBypassFlag;
  }
  vi.resetModules();
  oidcMiddlewareSpy.mockClear();
});

// ---------------------------------------------------------------------------

describe('GET /api/me — dev-auth bypass (DEV_AUTH_BYPASS=true)', () => {
  beforeEach(() => {
    process.env.NODE_ENV = 'test';
    process.env.DEV_AUTH_BYPASS = 'true';
  });

  it('returns 200 with the injected dev user identity', async () => {
    // Import AFTER setting env — index.ts reads env at module load time.
    const { app } = await import('../../src/index.js');
    const { DEV_USER } = await import('../../src/auth/devBypass.js');

    const res = await app.request('/api/me');
    expect(res.status).toBe(200);

    const body = (await res.json()) as { user: { id: number; displayName: string; color: string } };
    expect(body).toHaveProperty('user');
    expect(body.user.id).toBe(DEV_USER.id);
    expect(body.user.displayName).toBe(DEV_USER.displayName);
    expect(body.user.color).toBe(DEV_USER.color);
  });

  it('returns id=1 and color=#4A90D9 (first palette slot)', async () => {
    const { app } = await import('../../src/index.js');

    const res = await app.request('/api/me');
    expect(res.status).toBe(200);

    const body = (await res.json()) as { user: { id: number; color: string } };
    expect(body.user.id).toBe(1);
    expect(body.user.color).toBe('#4A90D9');
  });

  it('does not invoke oidcAuthMiddleware on /api/* when bypass is active', async () => {
    const { app } = await import('../../src/index.js');

    // Hit any /api/* route to trigger the middleware stack.
    await app.request('/api/me');

    // oidcAuthMiddleware() factory must NOT have been called — index.ts skips it.
    expect(oidcMiddlewareSpy).not.toHaveBeenCalled();
  });
});

describe('GET /api/me — OIDC path (no DEV_AUTH_BYPASS)', () => {
  beforeEach(() => {
    process.env.NODE_ENV = 'test';
    delete process.env.DEV_AUTH_BYPASS;
  });

  it('wires oidcAuthMiddleware on /api/* when bypass is not active', async () => {
    // Import app — devBypassActive will be false, so oidcAuthMiddleware() is called
    // during app construction (index.ts registers it via app.use('/api/*', ...)).
    await import('../../src/index.js');

    // The spy wraps the oidcAuthMiddleware() factory call in index.ts.
    // It must have been called exactly once (one app.use registration).
    expect(oidcMiddlewareSpy).toHaveBeenCalledTimes(1);
  });

  it('returns 401 when no OIDC session is present (getAuth returns null)', async () => {
    const { app } = await import('../../src/index.js');

    // oidcAuthMiddleware is mocked as a passthrough; getAuth is mocked to return null.
    // me.ts falls through to the getAuth path and returns 401.
    const res = await app.request('/api/me');
    expect(res.status).toBe(401);
    const body = (await res.json()) as { error: string };
    expect(body.error).toBe('Unauthorized');
  });
});
