import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import type { IdentityConfigurationRevision } from '@easyai-ai-gateway/contracts';
import { GatewayApiError } from '../../api';
import { UnifiedIdentityPanel } from './UnifiedIdentityPanel';
import {
  IdentityRuntimeActions,
  PairingProgressCard,
  executeIdentityOperation,
  isStaleIdentityOperation,
  pairingFailureMessage,
  requiresLocalTenantMapping,
} from './UnifiedIdentityPanel';

describe('UnifiedIdentityPanel', () => {
  it('asks only for the standard pairing inputs and does not expose a machine secret field', () => {
    const html = renderToStaticMarkup(<UnifiedIdentityPanel token="manager-token" />);

    expect(html).toContain('Auth Center 地址');
    expect(html).toContain('一次性接入码');
    expect(html).toContain('Gateway API 公网地址');
    expect(html).not.toContain('value="/gateway-api"');
    expect(html).toContain('Gateway Web 地址');
    expect(html).toContain('Gateway 本地租户映射（仅单租户 V1）');
    expect(html).toContain('多租户 V2 无需填写');
    const localTenantInput = html.match(/<input[^>]+placeholder="default"[^>]*>/)?.[0] ?? '';
    expect(localTenantInput).not.toContain('required');
    expect(localTenantInput).not.toContain('value="default"');
    expect(html).not.toContain('Machine Client Secret');
    expect(html).not.toContain('SSF Transmitter Issuer');
  });
});

describe('local tenant mapping compatibility', () => {
  it('does not require a fixed mapping for multi-tenant V2 revisions', () => {
    expect(requiresLocalTenantMapping('multi_tenant')).toBe(false);
  });

  it('keeps the fixed mapping requirement for single-tenant V1 revisions', () => {
    expect(requiresLocalTenantMapping('single_tenant')).toBe(true);
  });
});

describe('PairingProgressCard', () => {
  it('shows a safe actionable discovery failure and a recovery action', () => {
    const html = renderToStaticMarkup(<PairingProgressCard loading={false} onCancel={() => undefined} pairing={{
      id: 'pairing', revisionId: 'revision', remoteExchangeId: 'exchange', status: 'credentials_saved',
      cleanupStatus: 'none', remoteVersion: 4, expiresAt: '2026-07-17T15:00:00+08:00', version: 7,
      lastErrorCategory: 'security_event_discovery_failed', createdAt: '2026-07-17T14:00:00+08:00', updatedAt: '2026-07-17T14:01:00+08:00',
    }} />);

		expect(html).toContain('无法获取或校验 SSF Discovery');
    expect(html).toContain('后台正在自动重试');
    expect(html).toContain('放弃并清理本次配对');
    expect(html).toContain('role="status"');
    expect(html).toContain('aria-busy="true"');
  });

	it('does not reflect an untrusted category into the page', () => {
		expect(pairingFailureMessage('token-value <script>alert(1)</script>')).toBe('统一认证配对步骤暂时失败');
		expect(pairingFailureMessage('secret_token_abcdef')).toBe('统一认证配对步骤暂时失败');
	});

	it('renders cancellation cleanup as resumable progress without another cancel button', () => {
    const html = renderToStaticMarkup(<PairingProgressCard loading={false} onCancel={() => undefined} pairing={{
      id: 'pairing', revisionId: 'revision', remoteExchangeId: 'exchange', status: 'cancelled',
      cleanupStatus: 'pending', remoteVersion: 4, expiresAt: '2026-07-17T15:00:00+08:00', version: 8,
      lastErrorCategory: 'cleanup_security_event_retirement_pending', createdAt: '2026-07-17T14:00:00+08:00', updatedAt: '2026-07-17T14:01:00+08:00',
    }} />);

    expect(html).toContain('正在清理已放弃的配对');
    expect(html).toContain('安全退役窗口');
    expect(html).not.toContain('放弃并清理本次配对');
	});

	it('marks an existing SSF owner conflict as action-required', () => {
		const html = renderToStaticMarkup(<PairingProgressCard loading={false} onCancel={() => undefined} onResolveConnectionConflict={() => undefined} pairing={{
			id: 'pairing', revisionId: 'revision', remoteExchangeId: 'exchange', status: 'credentials_saved',
			cleanupStatus: 'none', remoteVersion: 4, expiresAt: '2026-07-17T15:00:00+08:00', version: 7,
			lastErrorCategory: 'security_event_connection_conflict', createdAt: '2026-07-17T14:00:00+08:00', updatedAt: '2026-07-17T14:01:00+08:00',
		}} />);

		expect(html).toContain('不会通过自动重试恢复');
		expect(html).toContain('安全退役现有连接');
	});

	it('does not claim an unsafe credential handoff will recover automatically', () => {
		const html = renderToStaticMarkup(<PairingProgressCard loading={false} onCancel={() => undefined} pairing={{
			id: 'pairing', revisionId: 'revision', remoteExchangeId: 'exchange', status: 'credentials_saved',
			cleanupStatus: 'none', remoteVersion: 4, expiresAt: '2026-07-17T15:00:00+08:00', version: 7,
			lastErrorCategory: 'security_event_credential_handoff_unsafe', createdAt: '2026-07-17T14:00:00+08:00', updatedAt: '2026-07-17T14:01:00+08:00',
		}} />);

		expect(html).toContain('不会自动恢复');
		expect(html).toContain('原配置下断开旧连接');
		expect(html).not.toContain('后台正在自动重试');
		expect(html).toContain('放弃并清理本次配对');
	});

	it('shows safe retirement as automatic progress without another retirement action', () => {
		const html = renderToStaticMarkup(<PairingProgressCard loading={false} onCancel={() => undefined} onResolveConnectionConflict={() => undefined} pairing={{
			id: 'pairing', revisionId: 'revision', remoteExchangeId: 'exchange', status: 'credentials_saved',
			cleanupStatus: 'none', remoteVersion: 4, expiresAt: '2026-07-17T15:00:00+08:00', version: 7,
			lastErrorCategory: 'security_event_retirement_pending', createdAt: '2026-07-17T14:00:00+08:00', updatedAt: '2026-07-17T14:01:00+08:00',
		}} />);

		expect(html).toContain('正在安全退役，完成后本次配对会自动继续');
		expect(html).not.toContain('安全退役现有连接</button>');
	});
});

describe('identity confirmation freshness', () => {
	it('requires a fresh confirmation after conflict responses', () => {
		expect(isStaleIdentityOperation(new GatewayApiError({ message: 'conflict', status: 409 }))).toBe(true);
		expect(isStaleIdentityOperation(new GatewayApiError({ message: 'changed', status: 412 }))).toBe(true);
		expect(isStaleIdentityOperation(new GatewayApiError({ message: 'retry', status: 503 }))).toBe(false);
	});
});

describe('identity runtime operation completion', () => {
  it('treats a successful disable followed by refresh 401 as submitted and requests re-login', async () => {
    const onSuccess = vi.fn();
    const onRuntimeChanged = vi.fn();

    await expect(executeIdentityOperation({
      action: vi.fn().mockResolvedValue(undefined),
      onRuntimeChanged,
      onSuccess,
      refresh: vi.fn().mockRejectedValue(new GatewayApiError({ message: 'unauthorized', status: 401 })),
      runtimeChanged: true,
      success: '统一认证已禁用，本地管理登录继续可用。',
    })).resolves.toBe(true);
    expect(onSuccess).toHaveBeenCalledWith(expect.stringContaining('请重新登录'));
    expect(onRuntimeChanged).toHaveBeenCalledOnce();
  });

  it('treats a successful activation followed by refresh 401 as submitted and requests re-login', async () => {
    const onSuccess = vi.fn();
    const onRuntimeChanged = vi.fn();

    await expect(executeIdentityOperation({
      action: vi.fn().mockResolvedValue(undefined),
      onRuntimeChanged,
      onSuccess,
      refresh: vi.fn().mockRejectedValue(new GatewayApiError({ message: 'unauthorized', status: 401 })),
      runtimeChanged: true,
      success: '统一认证已激活，用户需要重新登录。',
    })).resolves.toBe(true);
    expect(onSuccess).toHaveBeenCalledWith(expect.stringContaining('请重新登录'));
    expect(onRuntimeChanged).toHaveBeenCalledOnce();
  });

  it('does not reverse a committed runtime change when the follow-up refresh is unavailable', async () => {
    const onSuccess = vi.fn();
    const onRuntimeChanged = vi.fn();

    await expect(executeIdentityOperation({
      action: vi.fn().mockResolvedValue(undefined),
      onRuntimeChanged,
      onSuccess,
      refresh: vi.fn().mockRejectedValue(new GatewayApiError({ message: 'unavailable', status: 503 })),
      runtimeChanged: true,
      success: '统一认证已热切换启用，无需重启。',
    })).resolves.toBe(true);
    expect(onSuccess).toHaveBeenCalledWith(expect.stringContaining('操作已提交'));
    expect(onSuccess).toHaveBeenCalledWith(expect.stringContaining('状态刷新失败'));
    expect(onSuccess).not.toHaveBeenCalledWith(expect.stringContaining('请重新登录'));
    expect(onRuntimeChanged).toHaveBeenCalledOnce();
  });
});

describe('IdentityRuntimeActions remote-resource safety', () => {
  it('hides active re-pairing for every active configuration', () => {
    const html = renderToStaticMarkup(<IdentityRuntimeActions
      loading={false}
      previous={null}
      onDisable={() => undefined}
      onValidate={() => undefined}
    />);

    expect(html).not.toContain('重新配对');
    expect(html).toContain('请先禁用统一认证');
    expect(html).toContain('OAuth/SSF 资源');
    expect(html).toContain('重新验证');
    expect(html).toContain('禁用统一认证');
  });

  it('replaces unsafe previous-version rollback with a new onboarding-code instruction', () => {
    const html = renderToStaticMarkup(<IdentityRuntimeActions
      loading={false}
      previous={identityRevision('superseded', ['session_revocation'])}
      onDisable={() => undefined}
      onValidate={() => undefined}
    />);

    expect(html).not.toContain('回滚上一版本');
    expect(html).toContain('不能直接回滚');
    expect(html).toContain('新的接入码');
  });
});

function identityRevision(
  state: IdentityConfigurationRevision['state'],
  capabilities: string[],
): IdentityConfigurationRevision {
  return {
    id: `${state}-revision`,
    state,
    schemaVersion: 1,
    tenantMode: 'single_tenant',
    authCenterUrl: 'https://auth.example.com',
    issuer: 'https://issuer.example.com',
    scopes: ['openid'],
    capabilities,
    rolePrefix: 'gateway.',
    localTenantKey: 'default',
    publicBaseUrl: 'https://api.gateway.example.com',
    webBaseUrl: 'https://gateway.example.com',
    jitEnabled: true,
    legacyJwtEnabled: false,
    tokenIntrospection: capabilities.includes('token_introspection'),
    sessionRevocation: capabilities.includes('session_revocation'),
    sessionIdleSeconds: 1800,
    sessionAbsoluteSeconds: 28800,
    sessionRefreshSeconds: 60,
    version: 1,
    createdAt: '2026-07-17T14:00:00+08:00',
    updatedAt: '2026-07-17T14:00:00+08:00',
  };
}
