import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DungeonRenderer } from '../DungeonRenderer';
import { type World } from '../../core/types';

// Mock Phaser
vi.mock('phaser', () => {
  const mockSprite = {
    setDepth: vi.fn().mockReturnThis(),
    setScale: vi.fn().mockReturnThis(),
    play: vi.fn().mockReturnThis(),
    setPosition: vi.fn().mockReturnThis(),
    setVisible: vi.fn().mockReturnThis(),
    destroy: vi.fn(),
  };

  const mockGraphics = {
    clear: vi.fn().mockReturnThis(),
    fillStyle: vi.fn().mockReturnThis(),
    fillRect: vi.fn().mockReturnThis(),
    lineStyle: vi.fn().mockReturnThis(),
    strokeRect: vi.fn().mockReturnThis(),
  };

  const mockContainer = {
    add: vi.fn().mockReturnThis(),
    setPosition: vi.fn().mockReturnThis(),
    setVisible: vi.fn().mockReturnThis(),
    setScrollFactor: vi.fn().mockReturnThis(),
    setDepth: vi.fn().mockReturnThis(),
  };

  const mockRectangle = {
    setStrokeStyle: vi.fn().mockReturnThis(),
    setInteractive: vi.fn().mockReturnThis(),
  };

  return {
    default: {
      GameObjects: {
        Sprite: vi.fn(() => mockSprite),
        Graphics: vi.fn(() => mockGraphics),
        Container: vi.fn(() => mockContainer),
        Rectangle: vi.fn(() => mockRectangle),
      },
      Scene: vi.fn(),
      Math: {
        Clamp: vi.fn((v, min, max) => Math.min(Math.max(v, min), max)),
      },
    },
  };
});

describe('DungeonRenderer', () => {
  let mockScene: any;
  let renderer: DungeonRenderer;
  let mockWorld: World;

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

    mockScene = {
      add: {
        graphics: vi.fn().mockReturnValue({
          clear: vi.fn(),
          fillStyle: vi.fn(),
          fillRect: vi.fn(),
        }),
        sprite: vi.fn(() => ({
          setDepth: vi.fn().mockReturnThis(),
          setScale: vi.fn().mockReturnThis(),
          play: vi.fn().mockReturnThis(),
          destroy: vi.fn(),
        })),
        container: vi.fn().mockReturnValue({
          add: vi.fn(),
          setPosition: vi.fn(),
          setVisible: vi.fn(),
          setScrollFactor: vi.fn(),
          setDepth: vi.fn(),
        }),
        rectangle: vi.fn().mockReturnValue({
          setStrokeStyle: vi.fn().mockReturnThis(),
          setInteractive: vi.fn().mockReturnThis(),
        }),
      },
      cameras: {
        main: {
          width: 800,
          height: 600,
        },
      },
      anims: {
        create: vi.fn(),
        exists: vi.fn().mockReturnValue(true),
        generateFrameNumbers: vi.fn(),
      },
      make: {
        tilemap: vi.fn().mockReturnValue({
          addTilesetImage: vi.fn().mockReturnValue({}),
          createLayer: vi.fn().mockReturnValue({
            setDepth: vi.fn(),
            forEachTile: vi.fn(),
          }),
          destroy: vi.fn(),
        }),
      },

    };

    mockWorld = {
      width: 10,
      height: 10,
      tiles: new Array(100).fill(0),
      actors: new Map(),
      exit: { x: 9, y: 9 },
    };

    renderer = new DungeonRenderer(mockScene);
  });

  it('should track and clear corpse sprites on floor initialization', () => {
    renderer.initializeFloor(mockWorld);


    // Spawn a couple of corpses
    renderer.spawnCorpse(1, 1, 'rat');
    renderer.spawnCorpse(2, 2, 'bat');

    // Get the mock sprites that were returned by scene.add.sprite
    const corpse1 = mockScene.add.sprite.mock.results[1].value;
    const corpse2 = mockScene.add.sprite.mock.results[2].value;

    expect(mockScene.add.sprite).toHaveBeenCalledTimes(3);

    // Initialize floor again (changing level)
    renderer.initializeFloor(mockWorld);


    // Verify destroy was called on both corpse sprites
    expect(corpse1.destroy).toHaveBeenCalledTimes(1);
    expect(corpse2.destroy).toHaveBeenCalledTimes(1);
  });
});
