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

describe('OIDC BFF navigation', () => {
  afterEach(() => {
    vi.unstubAllEnvs();
    vi.unstubAllGlobals();
    vi.resetModules();
  });

  it('lets the authentication center select context and binds returnTo', async () => {
    vi.stubEnv('VITE_GATEWAY_API_BASE_URL', 'https://gateway.example.com/gateway-api');
		vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
			ok: true,
			json: async () => ({ enabled: true, oidcLogin: true, contextTypes: ['platform', 'tenant'], loginUrl: '/api/v1/auth/oidc/login', logoutUrl: '/api/v1/auth/oidc/logout', status: 'active' }),
		}));
    const assign = vi.fn();
    vi.stubGlobal('window', {
      location: { pathname: '/workspace', search: '?tab=wallet', hash: '#balance', assign },
    });
    const { startOIDCLogin } = await import('./oidc');
    await startOIDCLogin();
    const target = new URL(String(assign.mock.calls[0]?.[0]));
    expect(target.pathname).toBe('/gateway-api/api/v1/auth/oidc/login');
    expect(target.searchParams.get('returnTo')).toBe('/workspace?tab=wallet#balance');
    expect(target.searchParams.has('contextType')).toBe(false);
    expect(target.searchParams.has('client_id')).toBe(false);
    expect(target.searchParams.has('code_challenge')).toBe(false);
  });

  it('resolves the production relative API base against the browser origin', async () => {
    vi.stubEnv('VITE_GATEWAY_API_BASE_URL', '/gateway-api');
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        enabled: true,
        oidcLogin: true,
        contextTypes: ['platform', 'tenant'],
        loginUrl: '/api/v1/auth/oidc/login',
        logoutUrl: '/api/v1/auth/oidc/logout',
        status: 'active',
      }),
    }));
    const assign = vi.fn();
    vi.stubGlobal('window', {
      location: { origin: 'https://ai.51easyai.com', pathname: '/workspace/overview', search: '', hash: '', assign },
    });

    const { startOIDCLogin } = await import('./oidc');
    await startOIDCLogin('tenant');

    const target = new URL(String(assign.mock.calls[0]?.[0]));
    expect(target.origin).toBe('https://ai.51easyai.com');
    expect(target.pathname).toBe('/gateway-api/api/v1/auth/oidc/login');
    expect(target.searchParams.get('returnTo')).toBe('/workspace/overview');
    expect(target.searchParams.get('contextType')).toBe('tenant');
  });

  it('submits logout as a top-level POST without exposing tokens', async () => {
    vi.stubEnv('VITE_GATEWAY_API_BASE_URL', 'https://gateway.example.com/gateway-api');
		vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
			ok: true,
			json: async () => ({ enabled: true, oidcLogin: true, loginUrl: '/api/v1/auth/oidc/login', logoutUrl: '/api/v1/auth/oidc/logout', status: 'active' }),
		}));
    const submit = vi.fn();
    const form = { method: '', action: '', style: { display: '' }, submit };
    const appendChild = vi.fn();
    vi.stubGlobal('document', { createElement: vi.fn(() => form), body: { appendChild } });
    const { startOIDCLogout } = await import('./oidc');
    await expect(startOIDCLogout()).resolves.toBe(true);
    expect(form.method).toBe('POST');
    expect(form.action).toBe('https://gateway.example.com/gateway-api/api/v1/auth/oidc/logout');
    expect(Object.keys(form)).not.toContain('accessToken');
    expect(submit).toHaveBeenCalledOnce();
  });

  it('turns an invalid login transaction callback into an explicit retry action', async () => {
    const replaceState = vi.fn();
    vi.stubGlobal('window', {
      location: { pathname: '/', search: '?oidcError=OIDC_LOGIN_INVALID', hash: '#top' },
      history: { replaceState },
    });
    const { consumeOIDCCallbackError } = await import('./oidc');

    expect(consumeOIDCCallbackError()).toEqual({
      code: 'OIDC_LOGIN_INVALID',
      message: '统一认证登录事务无效或已过期，请重新登录',
      retryable: true,
    });
    expect(replaceState).toHaveBeenCalledWith({}, '', '/#top');
  });

  it('explains a missing transaction cookie without retaining diagnostic query parameters', async () => {
    const replaceState = vi.fn();
    vi.stubGlobal('window', {
      location: {
        pathname: '/',
        search: '?tab=login&oidcError=OIDC_LOGIN_INVALID&oidcErrorReason=COOKIE_MISSING&oidcDiagnosticId=diag-123',
        hash: '',
      },
      history: { replaceState },
    });
    const { consumeOIDCCallbackError } = await import('./oidc');

    expect(consumeOIDCCallbackError()).toEqual({
      code: 'OIDC_LOGIN_INVALID',
      reason: 'COOKIE_MISSING',
      diagnosticId: 'diag-123',
      message: '浏览器未携带统一认证登录事务 Cookie，请允许 localhost Cookie 后重新登录',
      retryable: true,
    });
    expect(replaceState).toHaveBeenCalledWith({}, '', '/?tab=login');
  });

  it('does not offer transaction retry for a token exchange failure', async () => {
    vi.stubGlobal('window', {
      location: { pathname: '/', search: '?oidcError=OIDC_TOKEN_EXCHANGE_FAILED', hash: '' },
      history: { replaceState: vi.fn() },
    });
    const { consumeOIDCCallbackError } = await import('./oidc');

    expect(consumeOIDCCallbackError()).toEqual({
      code: 'OIDC_TOKEN_EXCHANGE_FAILED',
      message: '统一认证登录结果无效，请重新登录',
      retryable: false,
    });
  });
});

describe('login entry routing', () => {
  it('uses the authentication center directly when both login contexts are available', async () => {
    const { loginEntryAction } = await import('./oidc');

    expect(loginEntryAction({
      enabled: true,
      oidcLogin: true,
      contextTypes: ['platform', 'tenant'],
      status: 'active',
    })).toBe('oidc');
  });

  it('keeps the existing workspace login when OIDC login is disabled', async () => {
    const { loginEntryAction } = await import('./oidc');

    expect(loginEntryAction({
      enabled: false,
      oidcLogin: false,
      contextTypes: [],
      status: 'disabled',
    })).toBe('workspace');
  });
});
