import { describe, it, expect, vi, beforeEach } from 'vitest';
import Fastify from 'fastify';
import { fastifyAwilixPlugin } from '@fastify/awilix';
import { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod';

// Mock jose for auth plugin
vi.mock('jose', () => ({
  createRemoteJWKSet: vi.fn(() => 'mock-jwks'),
  jwtVerify: vi.fn().mockResolvedValue({
    payload: {
      sub: 'kc-1',
      email: 'test@example.com',
      preferred_username: 'testuser',
      realm_access: { roles: ['member'] },
      householdIds: ['hh1'],
    },
    protectedHeader: { alg: 'RS256' },
    key: {},
  }),
}));

// Mock the users repository module with a real class
const { mockUpsertFromToken, mockFindByKeycloakId } = vi.hoisted(() => ({
  mockUpsertFromToken: vi.fn(),
  mockFindByKeycloakId: vi.fn(),
}));
vi.mock('../../../src/modules/users/users.repository.js', () => ({
  UsersRepository: class MockUsersRepository {
    upsertFromToken = mockUpsertFromToken;
    findByKeycloakId = mockFindByKeycloakId;
  },
}));

import authPlugin from '../../../src/plugins/auth.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';

describe('users.routes', () => {
  async function buildTestApp() {
    const app = Fastify({ logger: false });
    app.setValidatorCompiler(validatorCompiler);
    app.setSerializerCompiler(serializerCompiler);
    await app.register(fastifyAwilixPlugin, {
      disposeOnClose: true,
      disposeOnResponse: true,
      strictBooleanEnforced: true,
    });
    await app.register(authPlugin);
    await app.register(usersRoutes);
    return app;
  }

  beforeEach(() => {
    vi.clearAllMocks();
    mockFindByKeycloakId.mockResolvedValue({ householdIds: ['hh1'] });
  });

  it('returns 404 when syncFromToken returns null', async () => {
    mockUpsertFromToken.mockResolvedValue(null);

    const app = await buildTestApp();
    await app.ready();

    const res = await app.inject({
      method: 'GET',
      url: '/api/v1/users/me',
      headers: { authorization: 'Bearer valid-token' },
    });

    expect(res.statusCode).toBe(404);

    await app.close();
  });

  it('handles ObjectId and Date objects in response', async () => {
    const mockUser = {
      _id: { toString: () => 'u-obj' },
      keycloakId: 'kc-1',
      displayName: 'testuser',
      email: 'test@example.com',
      householdIds: ['hh1'],
      defaultHouseholdId: 'hh1',
      createdAt: { toISOString: () => '2024-01-01T00:00:00.000Z' },
      updatedAt: { toISOString: () => '2024-01-02T00:00:00.000Z' },
    };
    mockUpsertFromToken.mockResolvedValue(mockUser);

    const app = await buildTestApp();
    await app.ready();

    const res = await app.inject({
      method: 'GET',
      url: '/api/v1/users/me',
      headers: { authorization: 'Bearer valid-token' },
    });

    expect(res.statusCode).toBe(200);
    const body = res.json();
    expect(body._id).toBe('u-obj');
    expect(body.createdAt).toBe('2024-01-01T00:00:00.000Z');
    expect(body.defaultHouseholdId).toBe('hh1');

    await app.close();
  });

  it('GET /api/v1/users/me syncs user from token and returns profile', async () => {
    const mockUser = {
      _id: 'u1',
      keycloakId: 'kc-1',
      displayName: 'testuser',
      email: 'test@example.com',
      householdIds: ['hh1'],
      defaultHouseholdId: null,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
    mockUpsertFromToken.mockResolvedValue(mockUser);

    const app = await buildTestApp();
    await app.ready();

    const res = await app.inject({
      method: 'GET',
      url: '/api/v1/users/me',
      headers: { authorization: 'Bearer valid-token' },
    });

    expect(res.statusCode).toBe(200);
    const body = res.json();
    expect(body.keycloakId).toBe('kc-1');
    expect(body.email).toBe('test@example.com');
    expect(mockUpsertFromToken).toHaveBeenCalledWith('kc-1', 'test@example.com', 'testuser');

    await app.close();
  });
});
