import combineDictArrays from "../../../../../utils/grouping/combineDictArrays.js";
import sortBy from "../../../../../utils/sorting/sortBy.js";
import findCentroid from './findCentroid';
export default function generateGrid(states, layout) {
  if (!states || states.length === 0 || !layout) {
    return undefined;
  }
  const [columnCount, rowCount] = layout;
  const segments = states.reduce((acc, state) => {
    const arr = Array.from({
      length: state.portionCount
    });
    const stateSegments = arr.map(() => ({
      ...state
    }));
    return [...acc, ...stateSegments];
  }, []);
  const grid = Array.from({
    length: columnCount
  }).map(() => Array.from({
    length: rowCount
  }));
  segments.forEach((segment, idx) => {
    const {
      fips,
      contestId
    } = segment;
    const x = Math.floor(idx / rowCount);
    const direction = x % 2 === 1 ? 'up' : 'down';
    const y = direction === 'up' ? rowCount - 1 - idx % rowCount : idx % rowCount;
    grid[x][y] = {
      meta: {
        idx,
        status: segment.status,
        direction,
        fips,
        contestId
      },
      style: {}
    };
  });
  const gridAsStates = grid.reduce((acc, column, xIdx) => {
    const newData = column.reduce((innerAcc, cell, yIdx) => {
      const {
        fips
      } = cell.meta;
      const cellData = {
        x: xIdx,
        y: yIdx,
        ...cell
      };
      if (innerAcc[fips]) {
        innerAcc[fips].push(cellData);
        return innerAcc;
      }

      // eslint-disable-next-line no-param-reassign
      innerAcc[fips] = [cellData];
      return innerAcc;
    }, {});
    return combineDictArrays(acc, newData);
  }, {});
  Object.entries(gridAsStates).forEach(([abbr, cells]) => {
    // Assign the delimeter
    const sortedCells = cells.sort((a, b) => a.meta.idx - b.meta.idx);
    const lastCell = sortedCells[sortedCells.length - 1];
    if (lastCell.meta.direction === 'up' && lastCell.y !== 0) {
      grid[lastCell.x][lastCell.y].meta.borderTop = '1px solid black';
    } else if (lastCell.meta.direction === 'down' && lastCell.y !== rowCount - 1) {
      grid[lastCell.x][lastCell.y].meta.borderBottom = '1px solid black';
    }

    // Assign the right edge
    const rightEdge = {};
    cells.forEach(cell => {
      const comparator = rightEdge[cell.y];
      const comparatee = cell.x;
      if (!comparator || comparatee > comparator) {
        rightEdge[cell.y] = cell.x;
      }
    });
    Object.entries(rightEdge).forEach(([y, x]) => {
      if (x === columnCount - 1) {
        return;
      }
      grid[x][y].meta.borderRight = true;
    });

    // Find the Corners
    let topLeft;
    let topRight;
    let bottomRight;
    let bottomLeft;
    const midpoint = rowCount / 2;

    // First pass: Find initial corners without considering the midpoint
    cells.forEach(cell => {
      const {
        x,
        y
      } = cell;

      // Top Left (smallest x and y)
      if (!topLeft || x <= topLeft.x && y <= topLeft.y) {
        topLeft = cell;
      }

      // Top Right (largest x and smallest y)
      if (!topRight || x >= topRight.x && y <= topRight.y) {
        topRight = cell;
      }

      // Bottom Right (largest x and y)
      if (!bottomRight || x >= bottomRight.x && y >= bottomRight.y) {
        bottomRight = cell;
      }

      // Bottom Left (smallest x and largest y)
      if (!bottomLeft || x <= bottomLeft.x && y >= bottomLeft.y) {
        bottomLeft = cell;
      }
    });

    // Second pass: Reassign corners based on midpoint
    cells.forEach(cell => {
      const {
        x,
        y
      } = cell;

      // Adjust Top Left if it's below midpoint and there's a better candidate above
      if (topLeft.y > midpoint && y <= midpoint || y < topLeft.y && x <= topLeft.x) {
        topLeft = cell;
      }

      // Adjust Top Right if it's below midpoint and there's a better candidate above
      if (topRight.y > midpoint && y <= midpoint || y < topRight.y && x >= topRight.x) {
        topRight = cell;
      }

      // Adjust Bottom Left if it's above midpoint and there's a better candidate below
      if (bottomLeft.y < midpoint && y >= midpoint || y > bottomLeft.y && x <= bottomLeft.x) {
        bottomLeft = cell;
      }

      // Adjust Bottom Right if it's above midpoint and there's a better candidate below
      if (bottomRight.y < midpoint && y >= midpoint || y > bottomRight.y && x >= bottomRight.x) {
        bottomRight = cell;
      }
    });
    const trueCentroid = findCentroid([topLeft, topRight, bottomRight, bottomLeft]);
    const horizontalOnCentroid = sortBy(cells.filter(({
      y
    }) => y === Math.floor(trueCentroid.y)), ['x']);
    const relCentSegment = grid[Math.floor(trueCentroid.x)][Math.floor(trueCentroid.y)];
    const centroidWidth = 1 + horizontalOnCentroid[horizontalOnCentroid.length - 1].x - horizontalOnCentroid[0].x;
    relCentSegment.meta.label = abbr;
    const leftPadding = `${(trueCentroid.x - Math.floor(trueCentroid.x)) * 100}%`;
    relCentSegment.meta.paddingLeft = leftPadding;
    const topPadding = `${(trueCentroid.y - Math.floor(trueCentroid.y)) * 100}%`;
    relCentSegment.meta.paddingTop = topPadding;

    // Assign labels
    relCentSegment.meta.width = centroidWidth;
  });
  return grid;
}