type Coord = [number, number];

function isInBounds(
  row: number,
  col: number,
  rows: number,
  cols: number,
): boolean {
  return row >= 0 && row < rows && col >= 0 && col < cols;
}

export function validateSubmittedPath(
  path: Coord[],
  puzzle: any
): boolean {
  const rows = puzzle.grid.length;
  const cols = puzzle.grid[0].length;

  const blocked = new Set(
    puzzle.blocks.map((b: any) => `${b.row},${b.col}`)
  );

  const checkpoints = [...puzzle.numbers].sort(
    (a: any, b: any) => a.value - b.value
  );

  if (checkpoints.length === 0) {
    return false;
  }

  const firstCheckpoint = checkpoints[0];
  const finalCheckpoint = checkpoints[checkpoints.length - 1];
  const [startRow, startCol] = path[0] || [];
  const [endRow, endCol] = path[path.length - 1] || [];

  if (startRow !== firstCheckpoint.row || startCol !== firstCheckpoint.col) {
    return false;
  }

  if (endRow !== finalCheckpoint.row || endCol !== finalCheckpoint.col) {
    return false;
  }

  const visited = new Set<string>();
  const totalPlayableCells = rows * cols - blocked.size;

  for (const [row, col] of path) {
    if (!isInBounds(row, col, rows, cols)) {
      return false;
    }

    const key = `${row},${col}`;

    if (blocked.has(key)) {
      return false;
    }

    if (visited.has(key)) {
      return false;
    }

    visited.add(key);
  }

  if (visited.size !== totalPlayableCells) {
    return false;
  }

  for (let i = 1; i < path.length; i++) {
    const [r1, c1] = path[i - 1];
    const [r2, c2] = path[i];

    const distance =
      Math.abs(r1 - r2) +
      Math.abs(c1 - c2);

    if (distance !== 1) {
      return false;
    }
  }

  let lastCheckpointIndex = -1;

  for (const checkpoint of checkpoints) {
    const checkpointIndex = path.findIndex(
      ([row, col]) => row === checkpoint.row && col === checkpoint.col
    );

    if (checkpointIndex === -1 || checkpointIndex <= lastCheckpointIndex) {
      return false;
    }

    lastCheckpointIndex = checkpointIndex;
  }

  return true;
}
