/* @vitest-environment jsdom */

import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect, test, vi } from 'vitest';

import type {
  AuthAuditLogEntry,
  AuthRiskBlockSummary,
  AuthSessionSummary,
  AuthUser,
} from '../../services/authService';
import { AccountModal } from './AccountModal';

const baseUser: AuthUser = {
  id: 'user-1',
  displayName: '138****8000',
  avatarUrl: null,
  publicUserCode: 'user-tester',
  phoneNumber: '13800138000',
  phoneNumberMasked: '138****8000',
  loginMethod: 'phone',
  bindingStatus: 'active',
  wechatBound: true,
  wechatAccount: 'wx-openid-bind-001',
};

function renderAccountModal(overrides?: {
  user?: AuthUser;
  entryMode?: 'settings' | 'account';
  riskBlocks?: AuthRiskBlockSummary[];
  sessions?: AuthSessionSummary[];
  auditLogs?: AuthAuditLogEntry[];
  onRevokeSession?: (session: AuthSessionSummary) => Promise<void>;
  revokingSessionIds?: string[];
  initialSection?:
    | 'appearance'
    | 'account'
    | 'security'
    | 'devices'
    | 'logs'
    | null;
}) {
  return render(
    <AccountModal
      user={overrides?.user ?? baseUser}
      isOpen
      entryMode={overrides?.entryMode ?? 'settings'}
      initialSection={overrides?.initialSection ?? null}
      platformTheme="light"
      riskBlocks={overrides?.riskBlocks ?? []}
      sessions={overrides?.sessions ?? []}
      auditLogs={overrides?.auditLogs ?? []}
      loadingRiskBlocks={false}
      loadingSessions={false}
      loadingAuditLogs={false}
      isHydratingSettings={false}
      isPersistingSettings={false}
      settingsError={null}
      onClose={vi.fn()}
      onPlatformThemeChange={vi.fn()}
      onLogout={vi.fn().mockResolvedValue(undefined)}
      onRefreshRiskBlocks={vi.fn().mockResolvedValue(undefined)}
      onLiftRiskBlock={vi.fn().mockResolvedValue(undefined)}
      onRefreshSessions={vi.fn().mockResolvedValue(undefined)}
      onLogoutAll={vi.fn().mockResolvedValue(undefined)}
      onRefreshAuditLogs={vi.fn().mockResolvedValue(undefined)}
      onRevokeSession={
        overrides?.onRevokeSession ?? vi.fn().mockResolvedValue(undefined)
      }
      revokingSessionIds={overrides?.revokingSessionIds ?? []}
      changePhoneCaptchaChallenge={null}
      onSendChangePhoneCode={vi.fn().mockResolvedValue({
        cooldownSeconds: 60,
        expiresInSeconds: 300,
      })}
      onChangePhone={vi.fn().mockResolvedValue(undefined)}
      onChangePassword={vi.fn().mockResolvedValue(undefined)}
    />,
  );
}

function buildSession(
  overrides: Partial<AuthSessionSummary> = {},
): AuthSessionSummary {
  return {
    sessionId: 'usess_1',
    sessionIds: ['usess_1'],
    sessionCount: 1,
    clientLabel: 'Windows / Chrome',
    ipMasked: '203.0.*.*',
    isCurrent: false,
    createdAt: '2026-05-01T10:00:00.000Z',
    lastSeenAt: '2026-05-01T10:30:00.000Z',
    expiresAt: '2026-06-01T10:30:00.000Z',
    ...overrides,
  };
}

test('settings header uses a generic title instead of the phone number', () => {
  renderAccountModal();

  expect(screen.getByRole('dialog', { name: '设置与账号安全' })).toBeTruthy();
  expect(screen.getByText('设置与账号安全')).toBeTruthy();
  expect(screen.queryByText('138****8000')).toBeNull();
  expect(screen.queryByText('选择要管理的内容')).toBeNull();
  expect(
    screen.queryByText('主题、账号与设备能力统一在独立面板中管理'),
  ).toBeNull();
  expect(screen.queryByText(/^安全状态$/)).toBeNull();
  expect(screen.queryByText(/^登录设备$/)).toBeNull();
  expect(screen.queryByText(/^操作记录$/)).toBeNull();
  expect(screen.queryByText('当前账号状态')).toBeNull();
  expect(screen.queryByText('当前主题')).toBeNull();
  expect(screen.queryByRole('button', { name: '退出登录' })).toBeNull();
  expect(screen.queryByRole('button', { name: '退出全部设备' })).toBeNull();
  expect(screen.getByRole('button', { name: /主题设置/u })).toBeTruthy();
  expect(screen.getByRole('button', { name: /账号与安全/u })).toBeTruthy();
  expect(screen.queryByRole('button', { name: /主题外观/u })).toBeNull();
  expect(screen.queryByRole('button', { name: /账号信息/u })).toBeNull();
});

test('direct account entry does not render the settings shell as another dialog', () => {
  renderAccountModal({ entryMode: 'account' });

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(accountDialog).toBeTruthy();
  expect(screen.queryByRole('dialog', { name: '设置与账号安全' })).toBeNull();
  expect(screen.queryByText('设置与账号安全')).toBeNull();
  expect(
    within(accountDialog).getByRole('button', { name: '关闭' }),
  ).toBeTruthy();
  expect(
    within(accountDialog).queryByRole('button', { name: '返回' }),
  ).toBeNull();
});

test('account panel uses compact binding cards and keeps logout actions at the bottom', () => {
  renderAccountModal({ entryMode: 'account' });

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(within(accountDialog).getByText('账号信息')).toBeTruthy();
  expect(within(accountDialog).queryByText('身份信息')).toBeNull();
  expect(
    within(accountDialog).queryByText(
      '统一查看身份、安全状态、登录设备与最近操作。',
    ),
  ).toBeNull();
  expect(within(accountDialog).queryByText('登录方式')).toBeNull();
  expect(within(accountDialog).getByText('绑定手机号')).toBeTruthy();
  expect(within(accountDialog).getByText('13800138000')).toBeTruthy();
  expect(within(accountDialog).queryByText('138****8000')).toBeNull();
  expect(within(accountDialog).getByText('绑定微信')).toBeTruthy();
  expect(within(accountDialog).getByText('wx-openid-bind-001')).toBeTruthy();

  const compactCards = accountDialog.querySelectorAll(
    '[data-account-binding-card]',
  );
  expect(compactCards).toHaveLength(2);
  expect(
    within(compactCards[0] as HTMLElement).getByRole('button', {
      name: '更换手机号',
    }),
  ).toBeTruthy();
  expect(
    within(compactCards[1] as HTMLElement).getByRole('button', {
      name: '更换微信号',
    }),
  ).toBeTruthy();

  const accountContent =
    accountDialog.querySelector('[data-account-content]') ?? accountDialog;
  expect(
    accountContent.lastElementChild?.getAttribute('data-account-actions'),
  ).toBe('true');
});

test('account actions open in independent panels instead of inline expansion', async () => {
  const user = userEvent.setup();

  renderAccountModal();

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(accountDialog).toBeTruthy();
  expect(
    within(accountDialog).getByRole('button', { name: '返回' }),
  ).toBeTruthy();
  expect(
    within(accountDialog).getByRole('button', { name: '更换手机号' }),
  ).toBeTruthy();
  expect(screen.queryByLabelText('新手机号')).toBeNull();

  await user.click(
    within(accountDialog).getByRole('button', { name: '更换手机号' }),
  );

  const changePhoneDialog = screen.getByRole('dialog', {
    name: '绑定新手机号',
  });
  expect(within(changePhoneDialog).getByLabelText('新手机号')).toBeTruthy();
  expect(within(changePhoneDialog).getByLabelText('验证码')).toBeTruthy();
});

test('nested settings panels keep back navigation without an extra close action', async () => {
  const user = userEvent.setup();

  renderAccountModal();

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  const accountHeader = accountDialog.firstElementChild as HTMLElement | null;
  expect(
    within(accountDialog).getByRole('button', { name: '返回' }),
  ).toBeTruthy();
  expect(accountHeader?.lastElementChild?.textContent?.includes('返回')).toBe(
    true,
  );
  expect(
    within(accountDialog).queryByRole('button', { name: '关闭' }),
  ).toBeNull();

  await user.click(
    within(accountDialog).getByRole('button', { name: '更换手机号' }),
  );

  const changePhoneDialog = screen.getByRole('dialog', {
    name: '绑定新手机号',
  });
  const changePhoneHeader =
    changePhoneDialog.firstElementChild as HTMLElement | null;
  expect(
    within(changePhoneDialog).getByRole('button', { name: '返回' }),
  ).toBeTruthy();
  expect(
    changePhoneHeader?.lastElementChild?.textContent?.includes('返回'),
  ).toBe(true);
  expect(
    within(changePhoneDialog).queryByRole('button', { name: '关闭' }),
  ).toBeNull();
});

test('settings overlays move focus away from inert triggers and restore it on back', async () => {
  const user = userEvent.setup();

  renderAccountModal();

  const accountTrigger = screen.getByRole('button', { name: /账号与安全/ });
  expect(document.activeElement).not.toBe(accountTrigger);

  await user.click(accountTrigger);

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  const accountBackButton = within(accountDialog).getByRole('button', {
    name: '返回',
  });

  await waitFor(() => {
    expect(document.activeElement).toBe(accountBackButton);
  });

  const changePhoneTrigger = within(accountDialog).getByRole('button', {
    name: '更换手机号',
  });
  await user.click(changePhoneTrigger);

  const changePhoneDialog = screen.getByRole('dialog', {
    name: '绑定新手机号',
  });
  const changePhoneBackButton = within(changePhoneDialog).getByRole('button', {
    name: '返回',
  });

  await waitFor(() => {
    expect(document.activeElement).toBe(changePhoneBackButton);
  });

  await user.click(changePhoneBackButton);

  await waitFor(() => {
    expect(document.activeElement).toBe(changePhoneTrigger);
  });

  await user.click(accountBackButton);

  await waitFor(() => {
    expect(document.activeElement).toBe(accountTrigger);
  });
});

test('account panel includes merged security devices and audit sections', async () => {
  const user = userEvent.setup();

  renderAccountModal({
    riskBlocks: [
      {
        scopeType: 'phone',
        title: '手机号保护',
        detail: '检测到异常验证行为，已开启保护。',
        remainingSeconds: 600,
        expiresAt: '2026-04-20T10:00:00.000Z',
      },
    ],
    sessions: [
      buildSession({
        sessionId: 'session-1',
        sessionIds: ['session-1'],
        sessionCount: 1,
        clientLabel: 'iPhone 15 Pro',
        isCurrent: true,
        createdAt: '2026-04-20T07:30:00.000Z',
        lastSeenAt: '2026-04-20T09:00:00.000Z',
        expiresAt: '2026-04-27T09:00:00.000Z',
        ipMasked: '10.0.*.*',
      }),
    ],
    auditLogs: [
      {
        id: 'log-1',
        eventType: 'phone_login',
        title: '登录成功',
        detail: '通过手机号验证码完成登录。',
        createdAt: '2026-04-20T08:00:00.000Z',
        ipMasked: '10.0.*.*',
        userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)',
      },
    ],
  });

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(within(accountDialog).getByText('安全状态')).toBeTruthy();
  expect(within(accountDialog).getByText('登录设备')).toBeTruthy();
  expect(within(accountDialog).getByText('操作记录')).toBeTruthy();
  expect(within(accountDialog).getByText('手机号保护')).toBeTruthy();
  expect(within(accountDialog).getByText('iPhone 15 Pro')).toBeTruthy();
  expect(within(accountDialog).getByText('登录成功')).toBeTruthy();
  expect(
    within(accountDialog).getByRole('button', { name: '退出登录' }),
  ).toBeTruthy();
  expect(
    within(accountDialog).getByRole('button', { name: '退出全部设备' }),
  ).toBeTruthy();
});

test('legacy nested section requests now open the merged account panel', () => {
  renderAccountModal({ initialSection: 'security' });

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(accountDialog).toBeTruthy();
  expect(within(accountDialog).getByText('安全状态')).toBeTruthy();
  expect(within(accountDialog).getByText('登录设备')).toBeTruthy();
  expect(within(accountDialog).getByText('操作记录')).toBeTruthy();
});

test('current merged session group hides kick action and shows count', async () => {
  const user = userEvent.setup();

  renderAccountModal({
    sessions: [
      buildSession({
        sessionId: 'usess_current',
        sessionIds: ['usess_current', 'usess_rotated'],
        sessionCount: 2,
        isCurrent: true,
      }),
    ],
  });

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  expect(within(accountDialog).getByText('2 个会话')).toBeTruthy();
  expect(
    within(accountDialog).queryByRole('button', { name: '踢下线' }),
  ).toBeNull();
});

test('remote merged session group can be revoked with loading state', async () => {
  const user = userEvent.setup();
  const onRevokeSession = vi.fn().mockResolvedValue(undefined);
  const remoteSession = buildSession({
    sessionId: 'usess_remote',
    sessionIds: ['usess_remote', 'usess_remote_rotated'],
    sessionCount: 2,
  });

  renderAccountModal({
    sessions: [remoteSession],
    onRevokeSession,
    revokingSessionIds: ['usess_remote'],
  });

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));

  const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
  const revokeButton = within(accountDialog).getByRole('button', {
    name: '处理中...',
  }) as HTMLButtonElement;
  expect(revokeButton.disabled).toBe(true);
  expect(within(accountDialog).getByText('2 个会话')).toBeTruthy();
  expect(onRevokeSession).not.toHaveBeenCalled();
});

test('remote session revoke passes the grouped session payload', async () => {
  const user = userEvent.setup();
  const onRevokeSession = vi.fn().mockResolvedValue(undefined);
  const remoteSession = buildSession({
    sessionId: 'usess_remote',
    sessionIds: ['usess_remote', 'usess_remote_rotated'],
    sessionCount: 2,
  });

  renderAccountModal({
    sessions: [remoteSession],
    onRevokeSession,
  });

  await user.click(screen.getByRole('button', { name: /账号与安全/ }));
  await user.click(
    within(screen.getByRole('dialog', { name: '账号信息' })).getByRole(
      'button',
      { name: '踢下线' },
    ),
  );

  expect(onRevokeSession).toHaveBeenCalledWith(remoteSession);
});
