/**
 * Tests for POST /api/auth/oauth/[provider] (tracker dashboard — proxy to platform-service).
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { NextRequest } from 'next/server';

import { POST } from '@/app/api/auth/oauth/[provider]/route';

const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);

function jsonResponse(data: unknown, status = 200) {
  return {
    ok: status >= 200 && status < 300,
    status,
    json: () => Promise.resolve(data),
  } as unknown as Response;
}

function mockRequest(body: object, headers: Record<string, string> = {}): NextRequest {
  const headerMap = new Map(Object.entries(headers));
  return {
    json: async () => body,
    headers: { get: (k: string) => headerMap.get(k.toLowerCase()) ?? null },
  } as unknown as NextRequest;
}

function callOAuth(req: NextRequest, provider = 'google') {
  return POST(req, { params: Promise.resolve({ provider }) });
}

describe('POST /api/auth/oauth/[provider] (tracker)', () => {
  beforeEach(() => mockFetch.mockReset());

  it('rejects requests without an idToken (400) and never calls the backend', async () => {
    const res = await callOAuth(mockRequest({}));

    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe('idToken required');
    expect(mockFetch).not.toHaveBeenCalled();
  });

  it('proxies the provider login and returns tokens on success', async () => {
    mockFetch.mockResolvedValue(jsonResponse({ accessToken: 'tok' }));

    const res = await callOAuth(mockRequest({ idToken: 'google-id-token' }), 'google');

    expect(res.status).toBe(200);
    expect((await res.json()).accessToken).toBe('tok');
    expect(mockFetch).toHaveBeenCalledWith(
      expect.stringContaining('/api/auth/oauth/google'),
      expect.objectContaining({ method: 'POST' })
    );
  });

  it('injects the x-product-id header value into the forwarded body', async () => {
    mockFetch.mockResolvedValue(jsonResponse({ accessToken: 'tok' }));

    await callOAuth(mockRequest({ idToken: 'tok' }, { 'x-product-id': 'chronomind' }));

    const forwardedBody = JSON.parse(mockFetch.mock.calls[0][1].body);
    expect(forwardedBody.productId).toBe('chronomind');
    expect(forwardedBody.idToken).toBe('tok');
  });

  it('forwards the platform-service error status', async () => {
    mockFetch.mockResolvedValue(jsonResponse({ error: 'unverified' }, 401));

    const res = await callOAuth(mockRequest({ idToken: 'tok' }));

    expect(res.status).toBe(401);
    expect((await res.json()).error).toBe('unverified');
  });

  it('uses a provider-specific default error message', async () => {
    mockFetch.mockResolvedValue(jsonResponse({}, 403));

    const res = await callOAuth(mockRequest({ idToken: 'tok' }), 'github');

    expect(res.status).toBe(403);
    expect((await res.json()).error).toBe('OAuth github login failed');
  });

  it('returns 502 when the platform-service is unreachable', async () => {
    mockFetch.mockImplementationOnce(() => Promise.reject(new Error('ECONNREFUSED')));

    const res = await callOAuth(mockRequest({ idToken: 'tok' }));

    expect(res.status).toBe(502);
    expect((await res.json()).error).toBe('Platform service unavailable');
  });
});
