import { expect } from "@esm-bundle/chai";
import { PostProcessor } from "../../src/generation/PostProcessing.js";
import { VoxelGrid } from "../../src/grid/VoxelGrid.js";

describe("Generation: PostProcessor", () => {
  let grid;

  beforeEach(() => {
    grid = new VoxelGrid(20, 5, 20);
  });

  it("CoA 1: ensureConnectivity should identify separate regions", () => {
    // Create two disconnected floor regions
    // Region 1: left side
    for (let x = 1; x < 5; x++) {
      for (let z = 1; z < 5; z++) {
        grid.setCell(x, 0, z, 1); // Floor
        grid.setCell(x, 1, z, 0); // Air
      }
    }

    // Region 2: right side (disconnected)
    for (let x = 15; x < 19; x++) {
      for (let z = 15; z < 19; z++) {
        grid.setCell(x, 0, z, 1); // Floor
        grid.setCell(x, 1, z, 0); // Air
      }
    }

    PostProcessor.ensureConnectivity(grid);

    // After processing, smaller regions should be filled
    // The grid should have connectivity ensured
    expect(grid).to.exist;
  });

  it("CoA 2: ensureConnectivity should keep largest region", () => {
    // Create one large region and one small region
    // Large region
    for (let x = 1; x < 10; x++) {
      for (let z = 1; z < 10; z++) {
        grid.setCell(x, 0, z, 1);
        grid.setCell(x, 1, z, 0);
      }
    }

    // Small region
    for (let x = 15; x < 17; x++) {
      for (let z = 15; z < 17; z++) {
        grid.setCell(x, 0, z, 1);
        grid.setCell(x, 1, z, 0);
      }
    }

    const smallRegionAirBefore = grid.getCell(15, 1, 15);

    PostProcessor.ensureConnectivity(grid);

    // Small region should be filled (no longer air)
    const smallRegionAfter = grid.getCell(15, 1, 15);
    // If connectivity was ensured, small region might be filled
    // (exact behavior depends on implementation)
    expect(smallRegionAfter).to.exist;
  });

  it("CoA 3: floodFill should collect all connected air tiles", () => {
    // Create a connected region
    for (let x = 1; x < 5; x++) {
      for (let z = 1; z < 5; z++) {
        grid.setCell(x, 0, z, 1);
        grid.setCell(x, 1, z, 0);
      }
    }

    const visited = new Set();
    const region = PostProcessor.floodFill(grid, 2, 1, 2, visited);

    // Should collect multiple tiles
    expect(region.length).to.be.greaterThan(1);
    expect(region).to.deep.include({ x: 2, y: 1, z: 2 });
    expect(region).to.deep.include({ x: 3, y: 1, z: 2 });
  });

  it("CoA 4: floodFill should not include disconnected tiles", () => {
    // Fill entire grid with solid first to isolate regions
    grid.fill(1);

    // Create two separate regions with proper floor setup
    // Region 1: connected tiles
    grid.setCell(1, 0, 1, 1); // Floor
    grid.setCell(1, 1, 1, 0); // Air
    grid.setCell(2, 0, 1, 1); // Floor
    grid.setCell(2, 1, 1, 0); // Air (connected)

    // Region 2: disconnected (surrounded by solid, no path to region 1)
    grid.setCell(10, 0, 10, 1); // Floor
    grid.setCell(10, 1, 10, 0); // Air (disconnected)

    const visited = new Set();
    const region = PostProcessor.floodFill(grid, 1, 1, 1, visited);

    // Should only include connected tiles from region 1
    expect(region.length).to.equal(2);
    expect(region).to.deep.include({ x: 1, y: 1, z: 1 });
    expect(region).to.deep.include({ x: 2, y: 1, z: 1 });
    expect(region).to.not.deep.include({ x: 10, y: 1, z: 10 });
  });

  it("CoA 5: floodFill should respect bounds", () => {
    // Start at edge
    grid.setCell(0, 0, 0, 1);
    grid.setCell(0, 1, 0, 0);

    const visited = new Set();
    const region = PostProcessor.floodFill(grid, 0, 1, 0, visited);

    // Should only include valid positions
    expect(region.length).to.be.greaterThanOrEqual(1);
    region.forEach((pos) => {
      expect(grid.isValidBounds(pos.x, pos.y, pos.z)).to.be.true;
    });
  });

  it("CoA 6: ensureConnectivity should handle empty grid", () => {
    // Grid with no floor regions
    grid.fill(0);

    // Should not throw
    expect(() => PostProcessor.ensureConnectivity(grid)).to.not.throw();
  });

  it("CoA 7: ensureConnectivity should handle single region", () => {
    // Create one connected region
    for (let x = 1; x < 10; x++) {
      for (let z = 1; z < 10; z++) {
        grid.setCell(x, 0, z, 1);
        grid.setCell(x, 1, z, 0);
      }
    }

    const airCountBefore = Array.from(grid.cells).filter((v, i) => {
      const y = Math.floor(i / (grid.size.x * grid.size.z)) % grid.size.y;
      return y === 1 && v === 0;
    }).length;

    PostProcessor.ensureConnectivity(grid);

    // Single region should remain intact
    const airCountAfter = Array.from(grid.cells).filter((v, i) => {
      const y = Math.floor(i / (grid.size.x * grid.size.z)) % grid.size.y;
      return y === 1 && v === 0;
    }).length;

    // Air count should be similar (allowing for minor changes)
    expect(airCountAfter).to.be.greaterThan(0);
  });
});
