import { describe, it, expect, vi, afterEach } from 'vitest';
import Fastify from 'fastify';
import type { FastifyInstance } from 'fastify';
import { registerAuthRoutes } from '../src/routes/auth.js';
import { errorHandler } from '../src/middleware/error-handler.js';
import type { SafeUser } from '../src/repositories/user.repository.js';

let app: FastifyInstance;

afterEach(async () => {
  if (app) await app.close();
});

function makeSafeUser(overrides?: Partial<SafeUser>): SafeUser {
  return {
    id: 'user-1',
    email: 'michal@example.com',
    name: 'Michal',
    role: 'user',
    provider: 'local',
    externalId: null,
    version: 1,
    createdAt: new Date(),
    updatedAt: new Date(),
    ...overrides,
  };
}

function createMockDeps() {
  return {
    authService: {
      login: vi.fn(async () => ({})),
      logout: vi.fn(async () => {}),
      findSession: vi.fn(async () => null),
      impersonate: vi.fn(async () => ({})),
    },
    userService: {
      count: vi.fn(async () => 1),
      create: vi.fn(async () => makeSafeUser()),
      list: vi.fn(async () => []),
      getById: vi.fn(async () => makeSafeUser()),
      getByEmail: vi.fn(async () => makeSafeUser()),
      delete: vi.fn(async () => {}),
    },
    groupService: {
      create: vi.fn(async () => ({})),
      list: vi.fn(async () => []),
      getById: vi.fn(async () => null),
      getByName: vi.fn(async () => null),
      update: vi.fn(async () => null),
      delete: vi.fn(async () => {}),
    },
    rbacDefinitionService: {
      create: vi.fn(async () => ({})),
      list: vi.fn(async () => []),
      getById: vi.fn(async () => null),
      getByName: vi.fn(async () => null),
      update: vi.fn(async () => null),
      delete: vi.fn(async () => {}),
    },
    rbacService: {
      canAccess: vi.fn(async () => false),
      canRunOperation: vi.fn(async () => false),
      getPermissions: vi.fn(async () => []),
    },
  };
}

describe('GET /api/v1/auth/me', () => {
  it('returns user identity for authenticated request', async () => {
    const deps = createMockDeps();
    deps.authService.findSession.mockResolvedValue({
      userId: 'user-1',
      expiresAt: new Date(Date.now() + 86400_000),
    });
    deps.userService.getById.mockResolvedValue(makeSafeUser({ id: 'user-1', name: 'Michal', email: 'michal@example.com' }));

    app = Fastify({ logger: false });
    app.setErrorHandler(errorHandler);
    registerAuthRoutes(app, deps as never);
    await app.ready();

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

    expect(res.statusCode).toBe(200);
    const body = res.json<{ id: string; email: string; name: string | null }>();
    expect(body.id).toBe('user-1');
    expect(body.email).toBe('michal@example.com');
    expect(body.name).toBe('Michal');
  });

  it('returns null name when user has no name', async () => {
    const deps = createMockDeps();
    deps.authService.findSession.mockResolvedValue({
      userId: 'user-1',
      expiresAt: new Date(Date.now() + 86400_000),
    });
    deps.userService.getById.mockResolvedValue(makeSafeUser({ name: null }));

    app = Fastify({ logger: false });
    app.setErrorHandler(errorHandler);
    registerAuthRoutes(app, deps as never);
    await app.ready();

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

    expect(res.statusCode).toBe(200);
    expect(res.json<{ name: string | null }>().name).toBeNull();
  });

  it('returns 401 for unauthenticated request', async () => {
    const deps = createMockDeps();

    app = Fastify({ logger: false });
    app.setErrorHandler(errorHandler);
    registerAuthRoutes(app, deps as never);
    await app.ready();

    const res = await app.inject({
      method: 'GET',
      url: '/api/v1/auth/me',
    });

    expect(res.statusCode).toBe(401);
  });

  it('returns 401 for invalid token', async () => {
    const deps = createMockDeps();
    deps.authService.findSession.mockResolvedValue(null);

    app = Fastify({ logger: false });
    app.setErrorHandler(errorHandler);
    registerAuthRoutes(app, deps as never);
    await app.ready();

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

    expect(res.statusCode).toBe(401);
  });
});
