// ==============================
// HTML要素を取得
// ==============================

// パズル盤面
const board = document.getElementById("board");

// 見本画像
const preview = document.getElementById("preview");

// スタートボタン
const startBtn = document.getElementById("startBtn");

// ヘッダーの結果表示エリア
const resultHeader = document.getElementById("resultHeader");

// クリアメッセージ
const clearMsg = document.getElementById("clearMsg");

// ベスト記録表示エリア
const bestRecord = document.getElementById("bestRecord");

// リスタートボタン
const restartBtn = document.getElementById("restartBtn");

// 今回の記録を表示する要素
const thisRecordEl = document.getElementById("thisRecord");

// 新記録メッセージ表示要素
const newRecordMsg = document.getElementById("newRecordMsg");

// ==============================
// 定数
// ==============================

// localStorage に保存するときのキー名
const STORAGE_KEY = "puzzle_best";

// ==============================
// ゲームの状態を表す変数
// ==============================

// 4×4 のパズル
let grid = 4;

// タイルの並び順を管理する配列
let tiles = [];

// ゲーム開始時刻
let startTime = 0;

// プレイ中かどうか
let playing = false;

// ==============================
// ドラッグ操作用の変数
// ==============================

// 今つかんでいるピースの index
let dragIndex = -1;

// ドラッグ中に表示するコピー要素
let dragClone = null;

// ドロップ先として現在重なっているピースの index
let hoverIndex = -1;

// どの pointer を追跡するかを保存する
let pointerId = null;

// ==============================
// ベスト記録を表示する関数
// ==============================
function loadBest() {
  const best = localStorage.getItem(STORAGE_KEY);

  if (best !== null) {
    bestRecord.style.display = "block";
    bestRecord.textContent = "さいたんきろく🥇：" + best + "秒";
  } else {
    bestRecord.style.display = "none";
    bestRecord.textContent = "";
  }
}

// ==============================
// タイルをシャッフルする関数
// 完成状態のままにならないようにする
// ==============================
function shuffleTiles() {
  tiles = Array.from({ length: grid * grid }, (_, i) => i);

  do {
    for (let i = tiles.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [tiles[i], tiles[j]] = [tiles[j], tiles[i]];
    }
  } while (isSolved());
}

// ==============================
// 完成しているか判定する関数
// ==============================
function isSolved() {
  for (let i = 0; i < tiles.length; i++) {
    if (tiles[i] !== i) return false;
  }
  return true;
}

// ==============================
// ゲーム開始処理
// ==============================
function startGame() {
  // 前回の結果表示を消す
  resultHeader.style.display = "none";
  newRecordMsg.style.display = "none";

  // ボタン表示を切り替える
  startBtn.style.display = "none";
  restartBtn.style.display = "none";

  // シャッフルして盤面描画
  shuffleTiles();
  drawBoard();

  // 開始時刻を保存
  startTime = Date.now();

  // プレイ開始
  playing = true;
}

// ==============================
// 盤面を描画する関数
// ==============================
function drawBoard() {
  board.innerHTML = "";
  board.style.gridTemplateColumns = `repeat(${grid}, 1fr)`;

  const size = board.clientWidth;
  const pieceSize = size / grid;

  tiles.forEach((tile, index) => {
    const piece = document.createElement("div");
    piece.className = "piece";

    const x = tile % grid;
    const y = Math.floor(tile / grid);

    piece.style.backgroundSize = `${size}px ${size}px`;
    piece.style.backgroundPosition = `${-x * pieceSize}px ${-y * pieceSize}px`;

    piece.dataset.index = index;
    piece.addEventListener("pointerdown", onPointerDown);

    board.appendChild(piece);
  });
}

// ==============================
// ピースを押したときの処理
// ==============================
function onPointerDown(e) {
  if (!playing) return;

  const piece = e.currentTarget;
  dragIndex = Number(piece.dataset.index);
  pointerId = e.pointerId;

  piece.setPointerCapture(pointerId);
  createDragClone(piece, e.clientX, e.clientY);
  piece.classList.add("drag-source");

  window.addEventListener("pointermove", onPointerMove, { passive: false });
  window.addEventListener("pointerup", onPointerUp, { passive: false });
  window.addEventListener("pointercancel", onPointerUp, { passive: false });
}

// ==============================
// ドラッグ中の処理
// ==============================
function onPointerMove(e) {
  if (dragIndex < 0 || e.pointerId !== pointerId) return;

  e.preventDefault();
  moveDragClone(e.clientX, e.clientY);

  const target = document.elementFromPoint(e.clientX, e.clientY);
  const targetPiece = target ? target.closest(".piece") : null;

  clearHover();

  if (targetPiece && board.contains(targetPiece)) {
    hoverIndex = Number(targetPiece.dataset.index);
    targetPiece.classList.add("drop-target");
  }
}

// ==============================
// ドラッグ終了時の処理
// ==============================
function onPointerUp(e) {
  if (dragIndex < 0 || e.pointerId !== pointerId) return;

  e.preventDefault();

  const fromIndex = dragIndex;
  const toIndex = hoverIndex;

  cleanupDrag();

  if (
    Number.isInteger(fromIndex) &&
    Number.isInteger(toIndex) &&
    fromIndex !== toIndex &&
    toIndex >= 0
  ) {
    swap(fromIndex, toIndex);
    drawBoard();
    checkClear();
  }
}

// ==============================
// ドラッグ中に表示するコピー要素を作る
// ==============================
function createDragClone(piece, clientX, clientY) {
  removeDragClone();

  const rect = piece.getBoundingClientRect();
  dragClone = piece.cloneNode(true);
  dragClone.classList.add("drag-clone");
  dragClone.style.width = `${rect.width}px`;
  dragClone.style.height = `${rect.height}px`;

  document.body.appendChild(dragClone);
  moveDragClone(clientX, clientY);
}

// ==============================
// ドラッグコピーをマウス・指の位置へ移動
// ==============================
function moveDragClone(clientX, clientY) {
  if (!dragClone) return;
  dragClone.style.left = `${clientX}px`;
  dragClone.style.top = `${clientY}px`;
}

// ==============================
// ドラッグコピーを削除
// ==============================
function removeDragClone() {
  if (dragClone) {
    dragClone.remove();
    dragClone = null;
  }
}

// ==============================
// ドロップ候補の強調表示を消す
// ==============================
function clearHover() {
  hoverIndex = -1;
  document.querySelectorAll(".piece.drop-target").forEach((piece) => {
    piece.classList.remove("drop-target");
  });
}

// ==============================
// ドラッグ処理の後始末
// ==============================
function cleanupDrag() {
  removeDragClone();
  clearHover();

  document.querySelectorAll(".piece.drag-source").forEach((piece) => {
    piece.classList.remove("drag-source");
  });

  window.removeEventListener("pointermove", onPointerMove);
  window.removeEventListener("pointerup", onPointerUp);
  window.removeEventListener("pointercancel", onPointerUp);

  dragIndex = -1;
  pointerId = null;
}

// ==============================
// tiles 配列の2つの値を入れ替える
// ==============================
function swap(a, b) {
  [tiles[a], tiles[b]] = [tiles[b], tiles[a]];
}

// ==============================
// 完成判定を行う
// ==============================
function checkClear() {
  if (!isSolved()) return;
  clearGame();
}

// ==============================
// クリア時の処理
// ==============================
function clearGame() {
  playing = false;
  cleanupDrag();

  const time = ((Date.now() - startTime) / 1000).toFixed(1);
  const best = localStorage.getItem(STORAGE_KEY);

  let isNewRecord = false;

  if (best === null || Number(time) < Number(best)) {
    localStorage.setItem(STORAGE_KEY, time);
    isNewRecord = best !== null;
  }

  thisRecordEl.textContent = `${time}秒`;
  newRecordMsg.style.display = isNewRecord ? "inline" : "none";

  loadBest();

  clearMsg.style.display = "inline";
  resultHeader.style.display = "flex";
  restartBtn.style.display = "block";
}

// スタートボタン
startBtn.addEventListener("click", startGame);

// リスタートボタン
restartBtn.addEventListener("click", startGame);

// ==============================
// 画面サイズ変更時の処理
// ==============================
window.addEventListener("resize", () => {
  if (tiles.length > 0) {
    drawBoard();
  }
});

// ==============================
// 初期化処理
// ==============================
function init() {
  loadBest();
  preview.style.backgroundImage = "";
  resultHeader.style.display = "none";
  restartBtn.style.display = "none";
}

init();