import express from 'express';
import request from 'supertest';
import { beforeEach, describe, expect, it, vi } from 'vitest';

const mocked = vi.hoisted(() => ({
  findUserById: vi.fn(),
  poolQuery: vi.fn(),
}));

vi.mock('../../models/user', () => ({
  findUserById: mocked.findUserById,
}));

vi.mock('../../db', () => ({
  default: {
    query: mocked.poolQuery,
  },
}));

describe('admin middleware', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.resetModules();
    delete process.env.ADMIN_EMAILS;
    delete process.env.ADMIN_USER_IDS;
    delete process.env.ENFORCE_ADMIN_ALLOWLIST;
    process.env.NODE_ENV = 'test';
  });

  it('fails closed in production when no admin allowlist is configured', async () => {
    process.env.NODE_ENV = 'production';
    mocked.findUserById.mockResolvedValue({
      id: 'user-1',
      email: 'admin@rainmakersports.app',
      is_weatherman: true,
      email_verified: true,
    });

    const { requireAdminAccess } = await import('../admin');

    const app = express();
    app.get('/admin', (req, _res, next) => {
      req.user = { userId: 'user-1', email: 'admin@rainmakersports.app' } as any;
      next();
    }, requireAdminAccess, (_req, res) => {
      res.json({ ok: true });
    });

    const res = await request(app).get('/admin');

    expect(res.status).toBe(403);
    expect(res.body).toEqual({ error: 'Admin allowlist not configured' });
  });

  it('allows an allowlisted admin in production', async () => {
    process.env.NODE_ENV = 'production';
    process.env.ADMIN_EMAILS = 'admin@rainmakersports.app';
    mocked.findUserById.mockResolvedValue({
      id: 'user-1',
      email: 'admin@rainmakersports.app',
      is_weatherman: true,
      email_verified: true,
    });

    const { requireAdminAccess } = await import('../admin');

    const app = express();
    app.get('/admin', (req, _res, next) => {
      req.user = { userId: 'user-1', email: 'admin@rainmakersports.app' } as any;
      next();
    }, requireAdminAccess, (_req, res) => {
      res.json({ ok: true });
    });

    const res = await request(app).get('/admin');

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true });
  });

  it('still enforces the allowlist when it is configured', async () => {
    process.env.NODE_ENV = 'production';
    process.env.ADMIN_EMAILS = 'other-admin@rainmakersports.app';
    mocked.findUserById.mockResolvedValue({
      id: 'user-1',
      email: 'admin@rainmakersports.app',
      is_weatherman: true,
      email_verified: true,
    });
    mocked.poolQuery.mockResolvedValue({ rows: [] });

    const { requireAdminAccess } = await import('../admin');

    const app = express();
    app.get('/admin', (req, _res, next) => {
      req.user = { userId: 'user-1', email: 'admin@rainmakersports.app' } as any;
      next();
    }, requireAdminAccess, (_req, res) => {
      res.json({ ok: true });
    });

    const res = await request(app).get('/admin');

    expect(res.status).toBe(403);
    expect(res.body).toEqual({ error: 'Admin allowlist required' });
  });

  it('allows forcing strict allowlist enforcement outside production', async () => {
    process.env.NODE_ENV = 'test';
    process.env.ENFORCE_ADMIN_ALLOWLIST = 'true';
    mocked.findUserById.mockResolvedValue({
      id: 'user-1',
      email: 'admin@rainmakersports.app',
      is_weatherman: true,
      email_verified: true,
    });

    const { requireAdminAccess } = await import('../admin');

    const app = express();
    app.get('/admin', (req, _res, next) => {
      req.user = { userId: 'user-1', email: 'admin@rainmakersports.app' } as any;
      next();
    }, requireAdminAccess, (_req, res) => {
      res.json({ ok: true });
    });

    const res = await request(app).get('/admin');

    expect(res.status).toBe(403);
    expect(res.body).toEqual({ error: 'Admin allowlist not configured' });
  });

  it('skips admin audit writes cleanly before the audit table migration exists', async () => {
    mocked.findUserById.mockResolvedValue({
      id: 'user-1',
      email: 'admin@rainmakersports.app',
      is_weatherman: true,
      email_verified: true,
    });
    mocked.poolQuery.mockRejectedValueOnce({ code: '42P01' });
    const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});

    const { requireAdminAccess, auditAdminAccess } = await import('../admin');

    const app = express();
    app.get('/admin', (req, _res, next) => {
      req.user = { userId: 'user-1', email: 'admin@rainmakersports.app' } as any;
      next();
    }, requireAdminAccess, auditAdminAccess('test-area'), (_req, res) => {
      res.json({ ok: true });
    });

    const res = await request(app).get('/admin');
    await new Promise((resolve) => setImmediate(resolve));

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true });
    expect(warnSpy).toHaveBeenCalledWith(
      '[admin-audit] Audit table missing. Skipping admin audit writes until the migration is applied.',
    );

    warnSpy.mockRestore();
  });
});
