/* @vitest-environment jsdom */

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

import { AuthUiContext } from '../auth/AuthUiContext';
import {
  RpgEntryHomeView,
  type RpgEntryHomeViewProps,
} from './RpgEntryHomeView';
import type { PlatformPublicGalleryCard } from './rpgEntryWorldPresentation';

const { mockGetRpgProfileWalletLedger } = vi.hoisted(() => ({
  mockGetRpgProfileWalletLedger: vi.fn(async () => ({
    entries: [
      {
        id: 'ledger-1',
        amountDelta: -1,
        balanceAfter: 29,
        sourceType: 'asset_operation_consume',
        createdAt: '2026-04-28T10:00:00Z',
      },
      {
        id: 'ledger-2',
        amountDelta: 30,
        balanceAfter: 30,
        sourceType: 'invite_invitee_reward',
        createdAt: '2026-04-28T09:00:00Z',
      },
    ],
  })),
}));

vi.mock('../../services/rpg-entry/rpgProfileClient', () => ({
  getRpgProfileWalletLedger: mockGetRpgProfileWalletLedger,
  getRpgProfileRechargeCenter: vi.fn(async () => ({
    walletBalance: 0,
    membership: {
      status: 'normal',
      tier: 'normal',
      startedAt: null,
      expiresAt: null,
      updatedAt: null,
    },
    pointProducts: [
      {
        productId: 'points_60',
        title: '60叙世币',
        priceCents: 600,
        kind: 'points',
        pointsAmount: 60,
        bonusPoints: 60,
        durationDays: 0,
        badgeLabel: '首充双倍',
        description: '首充送60叙世币',
        tier: 'normal',
      },
    ],
    membershipProducts: [
      {
        productId: 'member_month',
        title: '月卡',
        priceCents: 2800,
        kind: 'membership',
        pointsAmount: 0,
        bonusPoints: 0,
        durationDays: 30,
        badgeLabel: '',
        description: '30天会员',
        tier: 'month',
      },
    ],
    benefits: [
      {
        benefitName: '免叙世币回合数',
        normalValue: '30',
        monthValue: '100',
        seasonValue: '100',
        yearValue: '100',
      },
    ],
    latestOrder: null,
    hasPointsRecharged: false,
  })),
  createRpgProfileRechargeOrder: vi.fn(async () => ({
    order: {
      orderId: 'order-1',
      productId: 'points_60',
      productTitle: '60叙世币',
      kind: 'points',
      amountCents: 600,
      status: 'paid',
      paymentChannel: 'mock',
      paidAt: '2026-04-25T10:00:00Z',
      createdAt: '2026-04-25T10:00:00Z',
      pointsDelta: 120,
      membershipExpiresAt: null,
    },
    center: {
      walletBalance: 120,
      membership: {
        status: 'normal',
        tier: 'normal',
        startedAt: null,
        expiresAt: null,
        updatedAt: null,
      },
      pointProducts: [],
      membershipProducts: [],
      benefits: [],
      latestOrder: null,
      hasPointsRecharged: true,
    },
  })),
}));

vi.mock('../ResolvedAssetImage', () => ({
  ResolvedAssetImage: () => null,
}));

const originalMatchMedia = window.matchMedia;
const puzzlePublicEntry = {
  sourceType: 'puzzle',
  workId: 'puzzle-work-public-1',
  profileId: 'puzzle-profile-public-1',
  publicWorkCode: 'PZ-EPUBLIC1',
  ownerUserId: 'user-2',
  authorDisplayName: '拼图玩家',
  worldName: '奇幻拼图',
  subtitle: '拼图关卡',
  summaryText: '一张用于公开分享的拼图作品。',
  coverImageSrc: null,
  themeTags: ['奇幻'],
  likeCount: 12,
  visibility: 'published',
  publishedAt: '1777110165.990127Z',
  updatedAt: '2026-04-25T10:00:00.000Z',
} satisfies PlatformPublicGalleryCard;

function mockDesktopLayout() {
  Object.defineProperty(window, 'matchMedia', {
    configurable: true,
    writable: true,
    value: vi.fn().mockImplementation(() => ({
      matches: true,
      media: '(min-width: 1024px)',
      onchange: null,
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      addListener: vi.fn(),
      removeListener: vi.fn(),
      dispatchEvent: vi.fn(),
    })),
  });
}

function renderProfileView(onRechargeSuccess = vi.fn()) {
  return render(
    <AuthUiContext.Provider
      value={{
        user: {
          id: 'user-1',
          publicUserCode: '100001',
          username: 'tester',
          displayName: '测试玩家',
          phoneNumberMasked: null,
          loginMethod: 'password',
          bindingStatus: 'active',
          wechatBound: false,
        },
        canAccessProtectedData: true,
        openLoginModal: vi.fn(),
        requireAuth: (action) => action(),
        openSettingsModal: vi.fn(),
        openAccountModal: vi.fn(),
        logout: vi.fn(async () => undefined),
        musicVolume: 0.42,
        setMusicVolume: vi.fn(),
        platformTheme: 'light',
        setPlatformTheme: vi.fn(),
        isHydratingSettings: false,
        isPersistingSettings: false,
        settingsError: null,
      }}
    >
      <RpgEntryHomeView
        activeTab="profile"
        onTabChange={vi.fn()}
        hasSavedGame={false}
        savedSnapshot={null}
        saveEntries={[]}
        saveError={null}
        featuredEntries={[]}
        latestEntries={[]}
        myEntries={[]}
        historyEntries={[]}
        profileDashboard={{
          walletBalance: 0,
          totalPlayTimeMs: 0,
          playedWorldCount: 0,
          updatedAt: null,
        }}
        isLoadingPlatform={false}
        isLoadingDashboard={false}
        isResumingSaveWorldKey={null}
        platformError={null}
        dashboardError={null}
        onContinueGame={vi.fn()}
        onResumeSave={vi.fn()}
        onOpenCreateWorld={vi.fn()}
        onOpenCreateTypePicker={vi.fn()}
        onOpenGalleryDetail={vi.fn()}
        onOpenLibraryDetail={vi.fn()}
        onSearchPublicCode={vi.fn()}
        onRechargeSuccess={onRechargeSuccess}
      />
    </AuthUiContext.Provider>,
  );
}

function renderLoggedOutHomeView(
  openLoginModal = vi.fn(),
  overrides: Partial<
    Pick<
      RpgEntryHomeViewProps,
      | 'featuredEntries'
      | 'latestEntries'
      | 'onOpenGalleryDetail'
      | 'onSearchPublicCode'
    >
  > = {},
) {
  return render(
    <AuthUiContext.Provider
      value={{
        user: null,
        canAccessProtectedData: false,
        openLoginModal,
        requireAuth: vi.fn(),
        openSettingsModal: vi.fn(),
        openAccountModal: vi.fn(),
        logout: vi.fn(async () => undefined),
        musicVolume: 0.42,
        setMusicVolume: vi.fn(),
        platformTheme: 'light',
        setPlatformTheme: vi.fn(),
        isHydratingSettings: false,
        isPersistingSettings: false,
        settingsError: null,
      }}
    >
      <RpgEntryHomeView
        activeTab="home"
        onTabChange={vi.fn()}
        hasSavedGame={false}
        savedSnapshot={null}
        saveEntries={[]}
        saveError={null}
        featuredEntries={overrides.featuredEntries ?? []}
        latestEntries={overrides.latestEntries ?? []}
        myEntries={[]}
        historyEntries={[]}
        profileDashboard={null}
        isLoadingPlatform={false}
        isLoadingDashboard={false}
        isResumingSaveWorldKey={null}
        platformError={null}
        dashboardError={null}
        onContinueGame={vi.fn()}
        onResumeSave={vi.fn()}
        onOpenCreateWorld={vi.fn()}
        onOpenCreateTypePicker={vi.fn()}
        onOpenGalleryDetail={overrides.onOpenGalleryDetail ?? vi.fn()}
        onOpenLibraryDetail={vi.fn()}
        onSearchPublicCode={overrides.onSearchPublicCode ?? vi.fn()}
      />
    </AuthUiContext.Provider>,
  );
}

afterEach(() => {
  vi.clearAllMocks();
  Object.defineProperty(window, 'matchMedia', {
    configurable: true,
    writable: true,
    value: originalMatchMedia,
  });
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: undefined,
  });
});

test('opens wallet ledger modal from narrative coin card', async () => {
  const user = userEvent.setup();

  renderProfileView();
  await user.click(screen.getByText('剩余叙世币'));

  expect(await screen.findByText('叙世币账单')).toBeTruthy();
  expect(mockGetRpgProfileWalletLedger).toHaveBeenCalledTimes(1);
  expect(screen.getByText('资产操作消耗')).toBeTruthy();
  expect(screen.getByText('-1')).toBeTruthy();
  expect(screen.getByText('填写邀请码奖励')).toBeTruthy();
  expect(screen.getByText('+30')).toBeTruthy();
});

test('wallet ledger modal shows empty and error states', async () => {
  const user = userEvent.setup();
  mockGetRpgProfileWalletLedger.mockResolvedValueOnce({ entries: [] });

  renderProfileView();
  await user.click(screen.getByText('剩余叙世币'));
  expect(await screen.findByText('暂无账单记录')).toBeTruthy();

  await user.click(screen.getByLabelText('关闭叙世币账单'));
  mockGetRpgProfileWalletLedger.mockRejectedValueOnce(new Error('加载失败'));
  await user.click(screen.getByText('剩余叙世币'));

  expect(await screen.findByText('加载失败')).toBeTruthy();
  expect(screen.getByText('重新加载')).toBeTruthy();
});

test('shows a reachable login entry in logged out mobile shell', async () => {
  const user = userEvent.setup();
  const openLoginModal = vi.fn();

  renderLoggedOutHomeView(openLoginModal);
  await user.click(screen.getByRole('button', { name: '登录' }));

  expect(openLoginModal).toHaveBeenCalledTimes(1);
});

test('mobile home search submits public work code', async () => {
  const user = userEvent.setup();
  const onSearchPublicCode = vi.fn();

  render(
    <AuthUiContext.Provider
      value={{
        user: null,
        canAccessProtectedData: false,
        openLoginModal: vi.fn(),
        requireAuth: vi.fn(),
        openSettingsModal: vi.fn(),
        openAccountModal: vi.fn(),
        logout: vi.fn(async () => undefined),
        musicVolume: 0.42,
        setMusicVolume: vi.fn(),
        platformTheme: 'light',
        setPlatformTheme: vi.fn(),
        isHydratingSettings: false,
        isPersistingSettings: false,
        settingsError: null,
      }}
    >
      <RpgEntryHomeView
        activeTab="home"
        onTabChange={vi.fn()}
        hasSavedGame={false}
        savedSnapshot={null}
        saveEntries={[]}
        saveError={null}
        featuredEntries={[]}
        latestEntries={[]}
        myEntries={[]}
        historyEntries={[]}
        profileDashboard={null}
        isLoadingPlatform={false}
        isLoadingDashboard={false}
        isResumingSaveWorldKey={null}
        platformError={null}
        dashboardError={null}
        onContinueGame={vi.fn()}
        onResumeSave={vi.fn()}
        onOpenCreateWorld={vi.fn()}
        onOpenCreateTypePicker={vi.fn()}
        onOpenGalleryDetail={vi.fn()}
        onOpenLibraryDetail={vi.fn()}
        onSearchPublicCode={onSearchPublicCode}
      />
    </AuthUiContext.Provider>,
  );

  const searchInput = screen.getByPlaceholderText(
    '输入 SY / CW / BF / PZ 编号',
  );
  await user.type(searchInput, 'PZ-PROFILE1{enter}');

  expect(onSearchPublicCode).toHaveBeenCalledWith('PZ-PROFILE1');
});

test('public gallery cards hide work code until detail is opened', async () => {
  const user = userEvent.setup();
  const onOpenGalleryDetail = vi.fn();

  renderLoggedOutHomeView(vi.fn(), {
    latestEntries: [puzzlePublicEntry],
    onOpenGalleryDetail,
  });

  expect(screen.queryByText('PZ-EPUBLIC1')).toBeNull();
  expect(
    screen.queryByRole('button', { name: '复制作品号 PZ-EPUBLIC1' }),
  ).toBeNull();

  await user.click(screen.getByRole('button', { name: /奇幻拼图/u }));

  expect(onOpenGalleryDetail).toHaveBeenCalledWith(puzzlePublicEntry);
});

test('desktop trending list shows kind instead of work code or timestamp text', () => {
  mockDesktopLayout();

  renderLoggedOutHomeView(vi.fn(), {
    latestEntries: [puzzlePublicEntry],
  });

  expect(screen.queryByText('PZ-EPUBLIC1')).toBeNull();
  expect(screen.getAllByText('拼图').length).toBeGreaterThan(0);
  expect(screen.queryByText('1777110165.990127Z')).toBeNull();
});
