/**
 * @vitest-environment happy-dom
 */
import React from 'react';
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { act, render } from '@testing-library/react';
import { LoadingIndicator } from '../../../components/common/LoadingIndicator.js';
import { Window } from 'happy-dom';

const advanceTimersBy = async (ms: number) => {
  await act(async () => {
    await vi.advanceTimersByTimeAsync(ms);
  });
};

beforeEach(() => {
  vi.useFakeTimers();
  const window = new Window();
  globalThis.window = window as any;
  globalThis.document = window.document as any;
});

afterEach(() => {
  vi.clearAllTimers();
  vi.useRealTimers();
});

describe('LoadingIndicator', () => {
  const getSpinnerText = (container: HTMLElement) => {
    return container.querySelector('ink-text')?.textContent ?? '';
  };

  const getMessageText = (container: HTMLElement) => {
    const texts = container.querySelectorAll('ink-text');
    return texts.length > 1 ? texts[1]?.textContent ?? '' : '';
  };

  it('does not render before the delay elapses', async () => {
    const { container } = render(
      <LoadingIndicator isLoading={true} message="読み込み中" delay={50} />
    );

    expect(container.textContent).toBe('');

    await advanceTimersBy(20);

    expect(container.textContent).toBe('');
  });

  it('renders after the delay elapses', async () => {
    const { container } = render(
      <LoadingIndicator isLoading={true} message="読み込み中" delay={30} />
    );

    await advanceTimersBy(30);

    expect(getMessageText(container)).toContain('読み込み中');
  });

  it('stops rendering when loading becomes false', async () => {
    const { container, rerender } = render(
      <LoadingIndicator isLoading={true} message="読み込み中" delay={10} />
    );

    await advanceTimersBy(10);

    expect(getMessageText(container)).toContain('読み込み中');

    await act(async () => {
      rerender(<LoadingIndicator isLoading={false} message="読み込み中" delay={10} />);
      await vi.advanceTimersByTimeAsync(0);
    });

    expect(container.textContent).toBe('');
  });

  it('cycles through spinner frames over time', async () => {
    const customFrames = ['.', '..', '...'];
    const { container } = render(
      <LoadingIndicator
        isLoading={true}
        message="読み込み中"
        delay={0}
        interval={5}
        frames={customFrames}
      />
    );

    await advanceTimersBy(0);

    const firstFrame = getSpinnerText(container);

    await advanceTimersBy(5);

    const secondFrame = getSpinnerText(container);

    await advanceTimersBy(5);

    const thirdFrame = getSpinnerText(container);

    expect(secondFrame).not.toEqual(firstFrame);
    expect(thirdFrame).not.toEqual(secondFrame);
    expect(customFrames).toContain(firstFrame ?? '');
    expect(customFrames).toContain(secondFrame ?? '');
    expect(customFrames).toContain(thirdFrame ?? '');
    expect(getMessageText(container)).toContain('読み込み中');
  });

  it('keeps rendering even when only a single frame is provided', async () => {
    const { container } = render(
      <LoadingIndicator
        isLoading={true}
        message="読み込み中"
        delay={0}
        interval={10}
        frames={['*']}
      />
    );

    await advanceTimersBy(0);
    expect(getSpinnerText(container)).toBe('*');

    await advanceTimersBy(30);
    expect(getSpinnerText(container)).toBe('*');
    expect(getMessageText(container)).toContain('読み込み中');
  });
});
