import React from 'react';
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import MoodMusic from '../components/MoodMusic';
import '@testing-library/jest-dom';

// Mock fetch for manifest endpoint
const mockManifest = [
  { mood: 'combat', file: 'combat.mp3', title: 'Battle Ready', license: 'CC0-1.0', source: 'freepd.com (via github.com/SoundSafari/CC0-1.0-Music)' },
  { mood: 'investigation', file: 'investigation.mp3', title: 'Dark Hallway', license: 'CC0-1.0', source: 'freepd.com (via github.com/SoundSafari/CC0-1.0-Music)' },
  { mood: 'tension', file: 'tension.mp3', title: 'Abstract Anxiety', license: 'CC0-1.0', source: 'freepd.com (via github.com/SoundSafari/CC0-1.0-Music)' },
  { mood: 'victory', file: 'victory.mp3', title: 'Heroic Adventure', license: 'CC0-1.0', source: 'freepd.com (via github.com/SoundSafari/CC0-1.0-Music)' },
  { mood: 'eerie', file: 'eerie.mp3', title: 'Ancient Rite', license: 'CC0-1.0', source: 'freepd.com (via github.com/SoundSafari/CC0-1.0-Music)' },
];

// Mock HTMLMediaElement (Audio constructor)
const mockAudio = {
  preload: 'auto',
  loop: true,
  volume: 0.3,
  src: '',
  pause: jest.fn(),
  play: jest.fn(() => Promise.resolve()),
  load: jest.fn(),
};

beforeEach(() => {
  jest.clearAllMocks();

  // Mock global fetch for manifest
  global.fetch = jest.fn(() =>
    Promise.resolve({
      ok: true,
      json: () => Promise.resolve(mockManifest),
    })
  );

  // Mock Audio constructor
  const OriginalAudio = global.Audio;
  global.Audio = jest.fn(() => mockAudio);
});

afterEach(() => {
  // Restore
  delete global.Audio;
});

describe('MoodMusic', () => {
  jest.setTimeout(10000);

  test('renders with scene music heading', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('Scene Music')).toBeInTheDocument();
    }, { timeout: 3000 });
  });

  test('fetches manifest and renders mood buttons', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    // Should have fetched the manifest
    expect(fetch).toHaveBeenCalledWith('/api/music/manifest');

    // Wait for buttons to appear
    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });

    // All moods should be present
    expect(screen.getByText('investigation')).toBeInTheDocument();
    expect(screen.getByText('tension')).toBeInTheDocument();
    expect(screen.getByText('victory')).toBeInTheDocument();
    expect(screen.getByText('eerie')).toBeInTheDocument();
  });

  test('clicking a mood button sets it as active and plays', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });

    // Click combat button
    await act(async () => {
      fireEvent.click(screen.getByText('combat'));
    });

    // Audio play should have been called
    await waitFor(() => {
      expect(mockAudio.play).toHaveBeenCalled();
    }, { timeout: 3000 });

    // Now playing label should show
    await waitFor(() => {
      expect(screen.getByText(/Battle Ready/)).toBeInTheDocument();
    }, { timeout: 3000 });

    // Stop button should appear
    expect(screen.getByText('■ Stop Music')).toBeInTheDocument();
  });

  test('clicking active mood again stops playback', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });

    // Click combat to start
    await act(async () => {
      fireEvent.click(screen.getByText('combat'));
    });

    await waitFor(() => {
      expect(mockAudio.play).toHaveBeenCalled();
    }, { timeout: 3000 });

    // Click combat again to stop
    await act(async () => {
      fireEvent.click(screen.getByText('combat'));
    });

    // Pause should have been called
    await waitFor(() => {
      expect(mockAudio.pause).toHaveBeenCalled();
    }, { timeout: 3000 });
  });

  test('stop button stops playback', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });

    // Start combat
    await act(async () => {
      fireEvent.click(screen.getByText('combat'));
    });

    await waitFor(() => {
      expect(screen.getByText('■ Stop Music')).toBeInTheDocument();
    }, { timeout: 3000 });

    // Click stop
    await act(async () => {
      fireEvent.click(screen.getByText('■ Stop Music'));
    });

    await waitFor(() => {
      expect(mockAudio.pause).toHaveBeenCalled();
    }, { timeout: 3000 });
  });

  test('volume slider is present and adjustable', async () => {
    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });

    const volumeSlider = document.querySelector('input[type="range"]');
    expect(volumeSlider).toBeInTheDocument();
    expect(volumeSlider).toHaveAttribute('min', '0');
    expect(volumeSlider).toHaveAttribute('max', '1');
  });

  test('shows loading state initially', async () => {
    // Delay the fetch response to test loading state
    global.fetch = jest.fn(() => new Promise((resolve) => {
      setTimeout(() => {
        resolve({
          ok: true,
          json: () => Promise.resolve(mockManifest),
        });
      }, 100);
    }));

    const { getByText, queryByText } = render(<MoodMusic />);

    // Initially should show loading
    expect(getByText('Loading music tracks...')).toBeInTheDocument();
    expect(queryByText('combat')).not.toBeInTheDocument();

    // After delay, should show buttons
    await waitFor(() => {
      expect(screen.getByText('combat')).toBeInTheDocument();
    }, { timeout: 3000 });
  });

  test('shows empty state when manifest is empty', async () => {
    global.fetch = jest.fn(() =>
      Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      })
    );

    await act(async () => {
      render(<MoodMusic />);
    });

    await waitFor(() => {
      expect(screen.getByText('No music tracks available.')).toBeInTheDocument();
    }, { timeout: 3000 });
  });
});