import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

import { defaultPreferences } from '../src/config';
import { isDarkTheme } from '../src/update-css-variables';

describe('preferences', () => {
  let PreferenceManager: typeof import('../src/preferences').PreferenceManager;
  let preferenceManager: InstanceType<
    typeof import('../src/preferences').PreferenceManager
  >;

  // 模拟 window.matchMedia 方法
  vi.stubGlobal(
    'matchMedia',
    vi.fn().mockImplementation((query) => ({
      addEventListener: vi.fn(),
      addListener: vi.fn(), // Deprecated
      dispatchEvent: vi.fn(),
      matches: query === '(prefers-color-scheme: dark)',
      media: query,
      onchange: null,
      removeEventListener: vi.fn(),
      removeListener: vi.fn(), // Deprecated
    })),
  );

  vi.stubGlobal('localStorage', {
    clear: vi.fn(),
    getItem: vi.fn(() => null),
    key: vi.fn(() => null),
    length: 0,
    removeItem: vi.fn(),
    setItem: vi.fn(),
  });

  vi.stubGlobal('sessionStorage', {
    clear: vi.fn(),
    getItem: vi.fn(() => null),
    key: vi.fn(() => null),
    length: 0,
    removeItem: vi.fn(),
    setItem: vi.fn(),
  });

  beforeAll(async () => {
    ({ PreferenceManager } = await import('../src/preferences'));
  });

  beforeEach(() => {
    vi.mocked(localStorage.getItem).mockImplementation(() => null);
    vi.mocked(localStorage.removeItem).mockReset();
    vi.mocked(localStorage.setItem).mockReset();
    vi.mocked(sessionStorage.getItem).mockImplementation(() => null);
    vi.mocked(sessionStorage.removeItem).mockReset();
    vi.mocked(sessionStorage.setItem).mockReset();
    preferenceManager = new PreferenceManager();
  });

  it('loads default preferences if no saved preferences found', () => {
    const preferences = preferenceManager.getPreferences();
    expect(preferences).toEqual(defaultPreferences);
  });

  it('initializes preferences with overrides', async () => {
    const overrides: any = {
      app: {
        locale: 'en-US',
      },
    };
    await preferenceManager.initPreferences({
      namespace: 'testNamespace',
      overrides,
    });

    // 等待防抖动操作完成
    // await new Promise((resolve) => setTimeout(resolve, 300)); // 等待100毫秒

    const expected = {
      ...defaultPreferences,
      app: {
        ...defaultPreferences.app,
        ...overrides.app,
      },
    };

    expect(preferenceManager.getPreferences()).toEqual(expected);
  });

  it('updates theme mode correctly', () => {
    preferenceManager.updatePreferences({
      theme: {
        mode: 'light',
      },
    });

    expect(preferenceManager.getPreferences().theme.mode).toBe('light');
  });

  it('updates color modes correctly', () => {
    preferenceManager.updatePreferences({
      app: { colorGrayMode: true, colorWeakMode: true },
    });

    expect(preferenceManager.getPreferences().app.colorGrayMode).toBe(true);
    expect(preferenceManager.getPreferences().app.colorWeakMode).toBe(true);
  });

  it('resets preferences to default', async () => {
    // 先更新一些偏好设置
    preferenceManager.updatePreferences({
      theme: {
        mode: 'light',
      },
    });

    // 然后重置偏好设置
    await preferenceManager.resetPreferences();

    expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
  });

  it('updates isMobile correctly', () => {
    // 模拟移动端状态
    vi.stubGlobal(
      'matchMedia',
      vi.fn().mockImplementation((query) => ({
        addEventListener: vi.fn(),
        addListener: vi.fn(),
        dispatchEvent: vi.fn(),
        matches: query === '(max-width: 768px)',
        media: query,
        onchange: null,
        removeEventListener: vi.fn(),
        removeListener: vi.fn(),
      })),
    );

    preferenceManager.updatePreferences({
      app: { isMobile: true },
    });

    expect(preferenceManager.getPreferences().app.isMobile).toBe(true);
  });

  it('updates the locale preference correctly', () => {
    preferenceManager.updatePreferences({
      app: { locale: 'en-US' },
    });

    expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
  });

  it('updates the sidebar width correctly', () => {
    preferenceManager.updatePreferences({
      sidebar: { width: 200 },
    });

    expect(preferenceManager.getPreferences().sidebar.width).toBe(200);
  });
  it('updates the sidebar collapse state correctly', () => {
    preferenceManager.updatePreferences({
      sidebar: { collapsed: true },
    });

    expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true);
  });
  it('updates the navigation style type correctly', () => {
    preferenceManager.updatePreferences({
      navigation: { styleType: 'flat' },
    } as any);

    expect(preferenceManager.getPreferences().navigation.styleType).toBe(
      'flat',
    );
  });

  it('resets preferences to default correctly', async () => {
    // 先更新一些偏好设置
    preferenceManager.updatePreferences({
      app: { locale: 'en-US' },
      sidebar: { collapsed: true, width: 200 },
      theme: {
        mode: 'light',
      },
    });

    // 然后重置偏好设置
    await preferenceManager.resetPreferences();

    expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
  });

  it('does not update undefined preferences', () => {
    const originalPreferences = preferenceManager.getPreferences();

    preferenceManager.updatePreferences({
      app: { nonexistentField: 'value' },
    } as any);

    expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
  });

  it('reverts to default when a preference field is deleted', () => {
    preferenceManager.updatePreferences({
      app: { locale: 'en-US' },
    });

    preferenceManager.updatePreferences({
      app: { locale: undefined },
    });

    expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
  });

  it('ignores updates with invalid preference value types', () => {
    const originalPreferences = preferenceManager.getPreferences();

    preferenceManager.updatePreferences({
      app: { isMobile: 'true' as unknown as boolean }, // 错误类型
    });

    expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
  });

  it('merges nested preference objects correctly', () => {
    preferenceManager.updatePreferences({
      app: { name: 'New App Name' },
    });

    const expected = {
      ...defaultPreferences,
      app: {
        ...defaultPreferences.app,
        name: 'New App Name',
      },
    };

    expect(preferenceManager.getPreferences()).toEqual(expected);
  });

  it('applies updates immediately after initialization', async () => {
    const overrides: any = {
      app: {
        locale: 'en-US',
      },
    };

    await preferenceManager.initPreferences({
      namespace: 'apply-updates',
      overrides,
    });

    preferenceManager.updatePreferences({
      theme: { mode: 'light' },
    });

    expect(preferenceManager.getPreferences().theme.mode).toBe('light');
  });

  it('initializes custom preferences extension with default values', async () => {
    const extension = {
      fields: [
        {
          component: 'switch',
          defaultValue: true,
          key: 'enableWorkbench',
          label: '启用工作台',
        },
        {
          component: 'select',
          defaultValue: 'single',
          key: 'tenantMode',
          label: '租户模式',
          options: [
            { label: '单租户', value: 'single' },
            { label: '多租户', value: 'multi' },
          ],
        },
      ],
      tabLabel: '扩展',
      title: '业务偏好',
    } as const;

    await preferenceManager.initPreferences({
      extension,
      namespace: 'custom-defaults',
    });

    expect(preferenceManager.getPreferencesExtension()).toEqual(extension);
    expect(preferenceManager.getCustomPreferences()).toEqual({
      enableWorkbench: true,
      tenantMode: 'single',
    });
  });

  it('does not expose mutable custom preference baselines or extension schema', async () => {
    const extension = {
      fields: [
        {
          component: 'number',
          componentProps: {
            max: 10,
            min: 2,
            step: 2,
          },
          defaultValue: 4,
          key: 'pageSize',
          label: '分页大小',
        },
      ],
      tabLabel: '扩展',
      title: '业务偏好',
    } as const;

    await preferenceManager.initPreferences({
      extension,
      namespace: 'custom-readonly',
    });

    const initialCustomPreferences =
      preferenceManager.getInitialCustomPreferences<{
        pageSize: number;
      }>() as { pageSize: number };
    const preferencesExtension = preferenceManager.getPreferencesExtension<{
      pageSize: number;
    }>() as {
      fields: Array<{ componentProps?: { max?: number }; label: string }>;
    };
    const [firstField] = preferencesExtension.fields;

    initialCustomPreferences.pageSize = 8;
    expect(firstField).toBeDefined();
    expect(firstField?.componentProps).toBeDefined();

    if (!firstField || !firstField.componentProps) {
      return;
    }

    firstField.label = '已修改';
    firstField.componentProps.max = 20;

    expect(preferenceManager.getInitialCustomPreferences()).toEqual({
      pageSize: 4,
    });
    expect(preferenceManager.getPreferencesExtension()).toEqual(extension);
  });

  it('updates and resets custom preferences correctly', async () => {
    await preferenceManager.initPreferences({
      extension: {
        fields: [
          {
            component: 'number',
            defaultValue: 20,
            key: 'pageSize',
            label: '分页大小',
          },
          {
            component: 'input',
            defaultValue: '日报',
            key: 'reportTitle',
            label: '报表标题',
          },
        ],
        tabLabel: '扩展',
      },
      namespace: 'custom-reset',
    });

    preferenceManager.updateCustomPreferences({
      pageSize: 50,
      reportTitle: '月报',
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 50,
      reportTitle: '月报',
    });

    await preferenceManager.resetPreferences();

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 20,
      reportTitle: '日报',
    });
  });

  it('ignores invalid custom preferences updates', async () => {
    await preferenceManager.initPreferences({
      extension: {
        fields: [
          {
            component: 'switch',
            defaultValue: true,
            key: 'enableWorkbench',
            label: '启用工作台',
          },
          {
            component: 'select',
            defaultValue: 'single',
            key: 'tenantMode',
            label: '租户模式',
            options: [
              { label: '单租户', value: 'single' },
              { label: '多租户', value: 'multi' },
            ],
          },
        ],
        tabLabel: '扩展',
      },
      namespace: 'custom-invalid',
    });

    const originalCustomPreferences = preferenceManager.getCustomPreferences();

    preferenceManager.updateCustomPreferences({
      enableWorkbench: 'true' as unknown as boolean,
      tenantMode: 'unknown',
      unknownField: 'value',
    } as any);

    expect(preferenceManager.getCustomPreferences()).toEqual(
      originalCustomPreferences,
    );
  });

  it('enforces custom number field min max and step constraints', async () => {
    await preferenceManager.initPreferences({
      extension: {
        fields: [
          {
            component: 'number',
            componentProps: {
              max: 10,
              min: 2,
              step: 2,
            },
            defaultValue: 4,
            key: 'pageSize',
            label: '分页大小',
          },
        ],
        tabLabel: '扩展',
      },
      namespace: 'custom-number-constraints',
    });

    preferenceManager.updateCustomPreferences({
      pageSize: 8,
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 8,
    });

    preferenceManager.updateCustomPreferences({
      pageSize: 1,
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 8,
    });

    preferenceManager.updateCustomPreferences({
      pageSize: 12,
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 8,
    });

    preferenceManager.updateCustomPreferences({
      pageSize: 5,
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 8,
    });
  });

  it('filters cached custom number values that violate field constraints', async () => {
    vi.mocked(localStorage.getItem).mockImplementation((key) => {
      if (key.endsWith('cache-preferences-custom')) {
        return JSON.stringify({
          value: {
            pageSize: 5,
          },
        });
      }

      return null;
    });

    await preferenceManager.initPreferences({
      extension: {
        fields: [
          {
            component: 'number',
            componentProps: {
              max: 10,
              min: 2,
              step: 2,
            },
            defaultValue: 4,
            key: 'pageSize',
            label: '分页大小',
          },
        ],
        tabLabel: '扩展',
      },
      namespace: 'custom-number-cache',
    });

    expect(preferenceManager.getCustomPreferences()).toEqual({
      pageSize: 4,
    });
  });
});

describe('isDarkTheme', () => {
  it('should return true for dark theme', () => {
    expect(isDarkTheme('dark')).toBe(true);
  });

  it('should return false for light theme', () => {
    expect(isDarkTheme('light')).toBe(false);
  });

  it('should return system preference for auto theme', () => {
    vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({
      addEventListener: vi.fn(),
      addListener: vi.fn(), // Deprecated
      dispatchEvent: vi.fn(),
      matches: query === '(prefers-color-scheme: dark)',
      media: query,
      onchange: null,
      removeEventListener: vi.fn(),
      removeListener: vi.fn(), // Deprecated
    }));

    expect(isDarkTheme('auto')).toBe(true);
    expect(window.matchMedia).toHaveBeenCalledWith(
      '(prefers-color-scheme: dark)',
    );
  });
});
