/**
 * Route-level tests for auth register provisioning behavior.
 */

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

const repoMock = {
  getByEmail: vi.fn(),
  create: vi.fn(),
  hashPassword: vi.fn(),
  updateLastLogin: vi.fn(),
  getById: vi.fn(),
};

const jwtMock = {
  createAccessToken: vi.fn(),
  createRefreshToken: vi.fn(),
  verifyToken: vi.fn(),
};

const productCacheMock = {
  getProduct: vi.fn(),
};

const subscriptionRepoMock = {
  createSubscription: vi.fn(),
};

const licenseRepoMock = {
  create: vi.fn(),
  generateKey: vi.fn(),
};

vi.mock('./repository.js', () => repoMock);
vi.mock('./jwt.js', () => jwtMock);
vi.mock('../products/cache.js', () => productCacheMock);
vi.mock('../subscriptions/repository.js', () => subscriptionRepoMock);
vi.mock('../licenses/repository.js', () => licenseRepoMock);
vi.mock('./login-events/repository.js', () => ({
  record: vi.fn().mockResolvedValue(undefined),
  countRecentFailures: vi.fn().mockResolvedValue(0),
}));
vi.mock('./login-events/risk-scorer.js', () => ({
  scoreLoginRisk: vi.fn().mockReturnValue({ score: 0, level: 'low', flags: [] }),
}));
vi.mock('./mfa/repository.js', () => ({
  getByUserId: vi.fn().mockResolvedValue(null),
}));
vi.mock('./mfa/challenge-store.js', () => ({
  createChallenge: vi.fn().mockReturnValue('mfa_test'),
}));
vi.mock('./devices/repository.js', () => ({
  getByFingerprint: vi.fn().mockResolvedValue(null),
  isDeviceTrusted: vi.fn().mockReturnValue(false),
}));
vi.mock('../../lib/config.js', () => ({
  config: {
    PORT: 4003,
    HOST: '0.0.0.0',
    NODE_ENV: 'test',
    SERVICE_NAME: 'platform-service',
    COSMOS_ENDPOINT: 'https://fake.documents.azure.com:443/',
    COSMOS_KEY: 'dGVzdA==',
    COSMOS_DATABASE: 'test',
    JWT_SECRET: 'test-secret-at-least-32-chars-long!!',
    MICROSOFT_TENANT_ID: 'common',
    WEBAUTHN_RP_ID: 'localhost',
    WEBAUTHN_RP_NAME: 'Test',
    USAGE_WARN_THRESHOLD: 0.8,
    BACKEND_URL: 'http://localhost:8000',
  },
}));
vi.mock('../../lib/event-bus.js', () => ({
  bus: { emit: vi.fn().mockResolvedValue(undefined) },
}));

describe('authRoutes register provisioning', () => {
  beforeEach(() => {
    vi.clearAllMocks();

    repoMock.getByEmail.mockResolvedValue(null);
    repoMock.hashPassword.mockResolvedValue('hash');
    repoMock.create.mockResolvedValue(undefined);

    jwtMock.createAccessToken.mockResolvedValue('access-token');
    jwtMock.createRefreshToken.mockResolvedValue('refresh-token');

    productCacheMock.getProduct.mockReturnValue({
      id: 'lysnrai',
      productId: 'lysnrai',
      displayName: 'LysnrAI',
      licensePrefix: 'LYSNR',
      packageName: '',
      defaultPlan: 'free',
      trialDays: 14,
      deviceLimits: { free: 1, pro: 3, enterprise: 10 },
      websiteUrl: '',
      status: 'active',
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    });

    subscriptionRepoMock.createSubscription.mockResolvedValue(undefined);
    licenseRepoMock.create.mockResolvedValue(undefined);
    licenseRepoMock.generateKey.mockReturnValue('LYSNR-AAAA-BBBB-CCCC');
  });

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

  it('provisions subscription/license from product defaults during register', async () => {
    const { authRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });

    await app.register(authRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'POST',
      url: '/api/auth/register',
      payload: {
        email: 'new@lysnrai.com',
        password: 'password123',
        displayName: 'New User',
        productId: 'lysnrai',
      },
    });

    expect(res.statusCode).toBe(201);
    const body = res.json();
    expect(body.accessToken).toBe('access-token');
    expect(body.refreshToken).toBe('refresh-token');
    expect(body.user.plan).toBe('free');

    expect(subscriptionRepoMock.createSubscription).toHaveBeenCalledTimes(1);
    const createdSub = subscriptionRepoMock.createSubscription.mock.calls[0][0];
    expect(createdSub.plan).toBe('free');
    expect(createdSub.status).toBe('trialing');

    expect(licenseRepoMock.create).toHaveBeenCalledTimes(1);
    const createdLicense = licenseRepoMock.create.mock.calls[0][0];
    expect(createdLicense.plan).toBe('free');
    expect(createdLicense.maxDevices).toBe(1);

    await app.close();
  });

  it('keeps register successful when provisioning fails (best-effort)', async () => {
    subscriptionRepoMock.createSubscription.mockRejectedValueOnce(new Error('sub create failed'));
    licenseRepoMock.create.mockRejectedValueOnce(new Error('license create failed'));

    const { authRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });

    await app.register(authRoutes, { prefix: '/api' });

    const res = await app.inject({
      method: 'POST',
      url: '/api/auth/register',
      payload: {
        email: 'new2@lysnrai.com',
        password: 'password123',
        displayName: 'New User 2',
        productId: 'lysnrai',
      },
    });

    expect(res.statusCode).toBe(201);
    const body = res.json();
    expect(body.accessToken).toBe('access-token');
    expect(body.refreshToken).toBe('refresh-token');

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