// @vitest-environment jsdom
import { cleanup, render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, describe, expect, it, vi } from 'vitest';

import type { InstanceContext } from '../app-shell.js';
import {
  NotificationsModule,
  type NotificationsDataSource,
  type NotificationsSnapshot,
} from './notifications-module.js';

afterEach(cleanup);

const owner: InstanceContext = {
  id: 'alpha',
  name: 'Alpha',
  origin: 'https://alpha.example',
  status: 'online',
  authentication: 'authenticated',
  freshness: 'fresh',
};

const snapshot: NotificationsSnapshot = {
  observedAt: '2026-07-17T12:00:00Z',
  channels: {
    status: 'healthy',
    total: 5,
    enabled: 4,
    disabled: 1,
    healthy: 3,
    degraded: 1,
    failed: 0,
    endpoint: 'https://private.example/hook',
    destination: '+15550000123',
    token: 'channel-secret',
    config: { provider: 'private-provider' },
  },
  queue: {
    status: 'processing',
    total: 18,
    pending: 4,
    processing: 2,
    delivered: 11,
    failed: 1,
    payload: 'private queue payload',
    destination: 'private@example.test',
  },
  logs: {
    status: 'available',
    total: 30,
    info: 20,
    warning: 7,
    error: 3,
    rawLogs: ['private raw log line'],
    phoneNumber: '+15550000456',
  },
};

function deferredSource() {
  let resolve!: (value: unknown) => void;
  let reject!: (reason: unknown) => void;
  const load = vi.fn<NotificationsDataSource['load']>(
    (_instanceId, _signal) =>
      new Promise((done, fail) => {
        void _instanceId;
        void _signal;
        resolve = done;
        reject = fail;
      }),
  );
  return {
    source: { load },
    load,
    resolve: (value: unknown) => resolve(value),
    reject: (reason: unknown) => reject(reason),
  };
}

describe('Notifications isolated safe read module', () => {
  it('reads only through the injected exact-owner source and renders allowlisted aggregates', async () => {
    const pending = deferredSource();
    render(<NotificationsModule instance={owner} dataSource={pending.source} />);

    expect(screen.getByRole('status', { name: '通知加载状态' })).toBeTruthy();
    expect(pending.load).toHaveBeenCalledWith('alpha', expect.any(AbortSignal));
    pending.resolve(snapshot);

    const channels = await screen.findByRole('region', { name: '渠道汇总' });
    expect(within(channels).getByText('正常', { selector: 'dd' })).toBeTruthy();
    expect(within(channels).getByText('5')).toBeTruthy();
    expect(within(screen.getByRole('region', { name: '队列汇总' })).getByText('18')).toBeTruthy();
    expect(within(screen.getByRole('region', { name: '日志汇总' })).getByText('30')).toBeTruthy();
    expect(screen.getByText(/观测时间：2026-07-17T12:00:00Z/)).toBeTruthy();
  });

  it('never exposes polymorphic config, endpoints, destinations, tokens, phone numbers, payloads, raw logs, or actions', async () => {
    render(<NotificationsModule instance={owner} dataSource={{ load: async () => snapshot }} />);
    expect(await screen.findByText(/仅显示汇总计数和状态/i)).toBeTruthy();

    const text = document.body.textContent ?? '';
    for (const secret of [
      'https://private.example/hook',
      '+15550000123',
      'channel-secret',
      'private-provider',
      'private queue payload',
      'private@example.test',
      'private raw log line',
      '+15550000456',
    ])
      expect(text).not.toContain(secret);
    expect(
      screen.queryByText(/endpoint|destination|token|phone number|payload|raw log/i),
    ).toBeNull();
    expect(screen.queryByRole('button')).toBeNull();
    expect(screen.queryByRole('link')).toBeNull();
  });

  it('fails closed without an injected source or authenticated owner', async () => {
    const load = vi.fn<NotificationsDataSource['load']>().mockResolvedValue(snapshot);
    const { rerender } = render(<NotificationsModule instance={owner} />);
    expect(screen.getByRole('status', { name: '通知不可用' }).textContent).toMatch(
      /没有可用的安全通知只读数据源.*未签订契约的生产端点/i,
    );

    rerender(
      <NotificationsModule
        instance={{ ...owner, authentication: 'auth-required' }}
        dataSource={{ load }}
      />,
    );
    expect(screen.getByRole('alert').textContent).toMatch(/需要先完成认证/i);
    expect(load).not.toHaveBeenCalled();
  });

  it('clears prior-owner data, aborts replaced reads, and fences late responses', async () => {
    const alpha = deferredSource();
    const bravo = deferredSource();
    const load = vi.fn<NotificationsDataSource['load']>((id, signal) =>
      id === 'alpha' ? alpha.source.load(id, signal) : bravo.source.load(id, signal),
    );
    const source = { load };
    const { rerender } = render(<NotificationsModule instance={owner} dataSource={source} />);
    const alphaSignal = load.mock.calls[0]?.[1];

    rerender(
      <NotificationsModule
        instance={{ ...owner, id: 'bravo', name: 'Bravo' }}
        dataSource={source}
      />,
    );
    expect(alphaSignal?.aborted).toBe(true);
    alpha.resolve(snapshot);
    bravo.resolve({
      ...snapshot,
      channels: { ...snapshot.channels, status: 'degraded', total: 9 },
    });
    expect(await screen.findByText('功能受限', { selector: 'dd' })).toBeTruthy();
    expect(screen.queryByText('正常', { selector: 'dd' })).toBeNull();
  });

  it('uses fixed errors and retains only same-owner last-good data across refresh failure and retry', async () => {
    const user = userEvent.setup();
    const load = vi
      .fn<NotificationsDataSource['load']>()
      .mockResolvedValueOnce(snapshot)
      .mockRejectedValueOnce(new Error('secret endpoint and token'))
      .mockResolvedValueOnce({ ...snapshot, channels: { status: 'available', total: 6 } });
    const source = { load };
    const { rerender } = render(
      <NotificationsModule instance={owner} dataSource={source} refreshSignal={0} />,
    );
    expect(await screen.findByText('正常', { selector: 'dd' })).toBeTruthy();

    rerender(<NotificationsModule instance={owner} dataSource={source} refreshSignal={1} />);
    const alert = await screen.findByRole('alert');
    expect(alert.textContent).toMatch(/刷新失败，正在显示上次已知的通知数据/i);
    expect(alert.textContent).not.toContain('secret endpoint');
    expect(screen.getByText('正常', { selector: 'dd' })).toBeTruthy();
    await user.click(screen.getByRole('button', { name: '重试加载通知数据' }));
    expect(
      await within(screen.getByRole('region', { name: '渠道汇总' })).findByText('可用'),
    ).toBeTruthy();
  });

  it('copies a bounded safe snapshot and drops malformed nested data and nominal-field secrets', async () => {
    const unsafe = {
      observedAt: 'api-token@example.test',
      channels: {
        status: 'channel-secret',
        total: -1,
        enabled: 1.5,
        disabled: 2,
        healthy: Number.POSITIVE_INFINITY,
      },
      queue: ['private payload'],
      logs: { status: { token: 'nested-secret' }, total: Number.MAX_SAFE_INTEGER + 1, error: 3 },
    };
    render(<NotificationsModule instance={owner} dataSource={{ load: async () => unsafe }} />);
    const logs = await screen.findByRole('region', { name: '日志汇总' });
    expect(within(logs).getByText('3')).toBeTruthy();
    expect(within(screen.getByRole('region', { name: '渠道汇总' })).getByText('2')).toBeTruthy();
    expect(document.body.textContent).not.toMatch(
      /api-token|channel-secret|private payload|nested-secret|Infinity|1\.5|-1/,
    );
    expect(screen.queryByText(/^观测时间：/)).toBeNull();
    unsafe.channels.disabled = 99;
    expect(screen.queryByText('99')).toBeNull();
  });

  it('uses a fixed initial-load error without rejection details', async () => {
    render(
      <NotificationsModule
        instance={owner}
        dataSource={{ load: async () => Promise.reject(new Error('raw log and private token')) }}
      />,
    );
    const alert = await screen.findByRole('alert');
    expect(alert.textContent).toContain('无法加载通知数据。');
    expect(alert.textContent).not.toContain('private token');
  });

  it('turns a synchronous source throw into the fixed initial-load error', async () => {
    render(
      <NotificationsModule
        instance={owner}
        dataSource={{
          load: () => {
            throw new Error('private notification token');
          },
        }}
      />,
    );
    const alert = await screen.findByRole('alert');
    expect(alert.textContent).toContain('无法加载通知数据。');
    expect(alert.textContent).not.toContain('private notification token');
  });
});
