import { App as AntApp, ConfigProvider } from 'antd';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import { afterEach, describe, expect, it, vi } from 'vitest';

import { AppContext, type AppContextValue } from '../../src/frontend/app-context';
import { I18nProvider } from '../../src/frontend/i18n/react';
import { detailHistoryState } from '../../src/frontend/navigation-state';
import { api } from '../../src/frontend/services/api';
import { mailhubTheme } from '../../src/frontend/theme';
import type { InboundMailbox, InboundMessage, RuntimeConfig } from '../../src/frontend/types';
import Inbox from '../../src/pages/Inbox';

describe('Inbox detail return path', () => {
  afterEach(() => vi.restoreAllMocks());

  it('keeps the original list history when another message is selected from an open detail', async () => {
    const user = userEvent.setup();
    const first = messageFixture(9, 1, 'Sent', 'First message');
    const second = messageFixture(10, 1, 'Sent', 'Second message');
    mockInboxApis([mailboxFixture(1)], [first, second]);
    vi.spyOn(api, 'inboundMessage').mockImplementation(async (id) => ({ message: id === second.id ? second : first }));
    const listPath = '/inbox?mailboxId=1&folder=Sent&page=2';
    const router = createInboxRouter(['/overview', listPath], 1);
    renderRouter(router);

    await user.click(await screen.findByRole('button', { name: 'First message · sender@example.test' }));
    await waitFor(() => expect(router.state.location.pathname).toBe('/inbox/messages/9'));
    expect(detailHistoryState(router.state.location.state)).toEqual({ listPath, depth: 1, origin: 'list' });

    fireEvent.click(screen.getByRole('button', { name: 'Second message · sender@example.test' }));
    await waitFor(() => expect(router.state.location.pathname).toBe('/inbox/messages/10'));
    expect(detailHistoryState(router.state.location.state)).toEqual({ listPath, depth: 2, origin: 'list' });

    fireEvent.click(screen.getByRole('button', { name: 'Close' }));
    await waitFor(() => expect(`${router.state.location.pathname}${router.state.location.search}`).toBe(listPath));

    await act(async () => {
      await router.navigate(-1);
    });
    expect(router.state.location.pathname).toBe('/overview');
  });

  it('keeps direct detail tabs navigable and closes them without reopening on Back', async () => {
    const user = userEvent.setup();
    const deepLinked = messageFixture(20, 2, 'Archive', 'Archived message');
    const messages = vi.fn(async () => ({ messages: [deepLinked], total: 1, page: 1, pageSize: 25 }));
    mockInboxApis([mailboxFixture(1), mailboxFixture(2)], [deepLinked]);
    vi.spyOn(api, 'inboundMessages').mockImplementation(messages);
    vi.spyOn(api, 'inboundMessage').mockResolvedValue({ message: deepLinked });
    const router = createInboxRouter(['/overview', '/inbox/messages/20'], 1);
    renderRouter(router);

    expect(await screen.findByText('Archived message')).toBeTruthy();
    await waitFor(() => {
      const params = new URLSearchParams(router.state.location.search);
      expect(params.get('mailboxId')).toBe('2');
      expect(params.get('folder')).toBe('Archive');
    });
    await waitFor(() => expect(messages).toHaveBeenCalledWith(expect.objectContaining({ mailboxId: 2, folder: 'Archive' })));

    await user.click(screen.getByRole('tab', { name: 'HTML 源码' }));
    await waitFor(() => expect(new URLSearchParams(router.state.location.search).get('tab')).toBe('html'));
    expect(detailHistoryState(router.state.location.state)).toEqual({
      listPath: '/inbox?mailboxId=2&folder=Archive',
      depth: 1,
      origin: 'direct'
    });

    await act(async () => {
      await router.navigate(-1);
    });
    expect(router.state.location.pathname).toBe('/inbox/messages/20');
    expect(new URLSearchParams(router.state.location.search).has('tab')).toBe(false);
    expect(detailHistoryState(router.state.location.state)).toBeNull();

    await act(async () => {
      await router.navigate(1);
    });
    expect(new URLSearchParams(router.state.location.search).get('tab')).toBe('html');
    expect(detailHistoryState(router.state.location.state)?.origin).toBe('direct');

    fireEvent.click(screen.getByRole('button', { name: 'Close' }));
    await waitFor(() => expect(router.state.location.pathname).toBe('/inbox'));
    const params = new URLSearchParams(router.state.location.search);
    expect(params.get('mailboxId')).toBe('2');
    expect(params.get('folder')).toBe('Archive');

    await act(async () => {
      await router.navigate(-1);
    });
    expect(router.state.location.pathname).toBe('/overview');
    expect(router.state.location.pathname).not.toContain('/messages/');
  });
});

function createInboxRouter(initialEntries: string[], initialIndex: number) {
  return createMemoryRouter([
    { path: '/inbox', element: <Inbox /> },
    { path: '/inbox/messages/:messageId', element: <Inbox /> },
    { path: '*', element: <div>other</div> }
  ], { initialEntries, initialIndex });
}

function renderRouter(router: ReturnType<typeof createInboxRouter>) {
  return render(
    <ConfigProvider theme={{ ...mailhubTheme, token: { ...mailhubTheme.token, motion: false } }}>
      <AntApp>
        <I18nProvider>
          <AppContext.Provider value={appContext}>
            <RouterProvider router={router} />
          </AppContext.Provider>
        </I18nProvider>
      </AntApp>
    </ConfigProvider>
  );
}

function mockInboxApis(mailboxes: InboundMailbox[], messages: InboundMessage[]) {
  vi.spyOn(api, 'domains').mockResolvedValue({ domains: [] });
  vi.spyOn(api, 'inboundMailboxes').mockResolvedValue({ mailboxes });
  vi.spyOn(api, 'inboundFolders').mockImplementation(async (mailboxId) => ({
    folders: [
      { name: 'INBOX', specialUse: null, messageCount: 0, unreadCount: 0 },
      { name: 'Sent', specialUse: '\\Sent', messageCount: mailboxId === 1 ? messages.length : 0, unreadCount: 0 },
      { name: 'Archive', specialUse: '\\Archive', messageCount: mailboxId === 2 ? messages.length : 0, unreadCount: 0 }
    ]
  }));
  vi.spyOn(api, 'inboundMessages').mockResolvedValue({ messages, total: messages.length, page: 1, pageSize: 25 });
}

function mailboxFixture(id: number): InboundMailbox {
  return {
    id,
    userId: 1,
    domainId: id,
    domain: `example-${id}.test`,
    address: `inbox-${id}@example-${id}.test`,
    localPart: `inbox-${id}`,
    displayName: `Inbox ${id}`,
    aliases: [],
    forwardTo: [],
    keepForwarded: true,
    quotaMb: 1024,
    passwordSet: true,
    passwordRecoverable: false,
    status: 'active',
    messageCount: 2,
    unreadCount: 0,
    createdAt: '2026-07-14T00:00:00.000Z',
    updatedAt: '2026-07-14T00:00:00.000Z'
  };
}

function messageFixture(id: number, mailboxId: number, folder: string, subject: string): InboundMessage {
  return {
    id,
    mailboxId,
    userId: 1,
    domainId: mailboxId,
    domain: `example-${mailboxId}.test`,
    mailboxAddress: `inbox-${mailboxId}@example-${mailboxId}.test`,
    folder,
    sender: 'sender@example.test',
    recipients: [`inbox-${mailboxId}@example-${mailboxId}.test`],
    subject,
    messageId: `<message-${id}@example.test>`,
    preview: `${subject} preview`,
    read: true,
    receivedAt: '2026-07-14T00:00:00.000Z',
    createdAt: '2026-07-14T00:00:00.000Z',
    updatedAt: '2026-07-14T00:00:00.000Z',
    textBody: `${subject} body`,
    htmlBody: `<p>${subject}</p>`,
    rawMessage: `Subject: ${subject}`
  };
}

const runtimeConfig: RuntimeConfig = {
  appBaseUrl: 'https://mail.example.test',
  mailHostname: 'mail.example.test',
  sendingIp: '192.0.2.10',
  defaultSpfMechanisms: '',
  dmarcPolicy: 'none',
  dmarcRua: '',
  sendRequiresVerified: true,
  engagementTrackingEnabled: true,
  listUnsubscribeMailto: '',
  listUnsubscribeUrl: '',
  listUnsubscribePostEnabled: false,
  feedbackIdEnabled: false,
  reportAbuseTo: '',
  csaComplaintsTo: '',
  bounceAddress: '',
  bounceEnvelopeEnabled: false
};

const appContext: AppContextValue = {
  user: { id: 1, username: 'admin', email: 'admin@example.test', role: 'admin', status: 'active' },
  config: runtimeConfig,
  refreshBootstrap: vi.fn(async () => undefined),
  logout: vi.fn(async () => undefined)
};
