<?php
declare(strict_types=1);

require_once __DIR__ . '/../lib/db.php';
require_once __DIR__ . '/../lib/helpers.php';

/* =========================
   Güvenli JSON çıktı
========================= */
if (!function_exists('json_out')) {
  function json_out(array $data, int $status = 200): void {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('X-Content-Type-Options: nosniff');
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
  }
}

/* =========================
   Session (helpers ile aynı)
========================= */
if (function_exists('start_session')) {
  start_session();
} else {
  if (session_status() !== PHP_SESSION_ACTIVE) session_start();
}

/* =========================
   DB + Config
========================= */
$db = db();
$c  = cfg();
$action = (string)($_GET['action'] ?? '');

/* =========================
   Küçük yardımcılar
========================= */
function require_uid(): int {
  if (empty($_SESSION['uid'])) json_out(['ok'=>false,'error'=>'login_required'], 401);
  return (int)$_SESSION['uid'];
}

function user_token_hash(int $uid): string {
  $appKey = (string)(cfg()['app_key'] ?? 'APPKEY_MISSING');
  return sha256('U|'.$uid.'|'.$appKey);
}

function add_minutes_ts(int $minutes): string {
  return date('Y-m-d H:i:s', time() + ($minutes * 60));
}

/**
 * Board device auth:
 * - Cookie'lerden board_id ve board_key okunur
 * - DB'de device_key_hash varsa sha256(cookie_key) ile eşleşmeli
 * - DB'de device_key_hash NULL ise ilk boot'ta set ederiz (bootstrap)
 */
function get_board_cookie(): array {
  $cb = trim((string)($_COOKIE['tahta_board_id'] ?? ''));
  $ck = trim((string)($_COOKIE['tahta_board_key'] ?? ''));
  return [$cb, $ck];
}

function fetch_board(mysqli $db, string $boardId): ?array {
  $sql = "SELECT id, board_id, device_key_hash, owner_user_id, pin_code,
                 pairing_nonce, pairing_expires_at,
                 unlock_code, unlock_nonce, unlock_expires_at,
                 is_frozen, paired_token_hash, paired_at,
                 created_at, updated_at
          FROM tahta_boards
          WHERE board_id=? LIMIT 1";
  $stmt = $db->prepare($sql);
  if (!$stmt) return null;
  $stmt->bind_param('s', $boardId);
  $stmt->execute();
  $stmt->bind_result(
    $id,$bid,$dkh,$owner,$pin,
    $pnonce,$pexp,
    $ucode,$unonce,$uexp,
    $frozen,$pth,$pat,
    $cat,$uat
  );

  $row = null;
  if ($stmt->fetch()) {
    $row = [
      'id' => (int)$id,
      'board_id' => (string)$bid,
      'device_key_hash' => $dkh !== null ? (string)$dkh : null,
      'owner_user_id' => $owner !== null ? (int)$owner : null,
      'pin_code' => (string)$pin,
      'pairing_nonce' => (string)$pnonce,
      'pairing_expires_at' => (string)$pexp,
      'unlock_code' => $ucode !== null ? (string)$ucode : null,
      'unlock_nonce' => $unonce !== null ? (string)$unonce : null,
      'unlock_expires_at' => $uexp !== null ? (string)$uexp : null,
      'is_frozen' => (int)$frozen,
      'paired_token_hash' => $pth !== null ? (string)$pth : null,
      'paired_at' => $pat !== null ? (string)$pat : null,
      'created_at' => (string)$cat,
      'updated_at' => (string)$uat,
    ];
  }
  $stmt->close();
  return $row;
}

function ensure_board(mysqli $db, string $boardId, string $cookieKeyRaw): array {
  $now = now_ts();
  $keyHash = sha256($cookieKeyRaw);

  $row = fetch_board($db, $boardId);

  // 1) Yoksa oluştur
  if (!$row) {
    $pin = random_code();
    $pnonce = random_nonce();
    $pexp = add_minutes_ts(10);

    $stmt = $db->prepare("
      INSERT INTO tahta_boards
        (board_id, device_key_hash, owner_user_id, pin_code, pairing_nonce, pairing_expires_at,
         unlock_code, unlock_nonce, unlock_expires_at, is_frozen,
         paired_token_hash, paired_at, created_at, updated_at)
      VALUES
        (?, ?, NULL, ?, ?, ?, NULL, NULL, NULL, 1, NULL, NULL, ?, ?)
    ");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    // ✅ FIX: Placeholder sayısı 7 (?,?,?,?,?,?,?) → bind_param da 7 olmalı
    // Aksi halde "Number of variables doesn't match number of parameters" uyarısı üretir
    // ve JSON çıktısını bozup front-end'de QR/kodların görünmemesine neden olur.
    $stmt->bind_param('sssssss', $boardId, $keyHash, $pin, $pnonce, $pexp, $now, $now);
    $stmt->execute();
    $stmt->close();

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_create_failed'], 500);
    return $row;
  }

  // 2) device_key_hash doğrula / ilk kez set et
  if (!empty($row['device_key_hash'])) {
    if (!hash_equals((string)$row['device_key_hash'], $keyHash)) {
      json_out(['ok'=>false,'error'=>'board_unauthorized'], 403);
    }
  } else {
    $stmt = $db->prepare("UPDATE tahta_boards SET device_key_hash=?, updated_at=? WHERE board_id=?");
    if ($stmt) {
      $stmt->bind_param('sss', $keyHash, $now, $boardId);
      $stmt->execute();
      $stmt->close();
    }
    $row = fetch_board($db, $boardId) ?: $row;
  }

  // 3) Sahipsizse pairing alanlarını garanti et
  $owned = !empty($row['owner_user_id']) || !empty($row['paired_at']);

  if (!$owned) {
    $expired = false;
    try {
      $expired = (strtotime((string)$row['pairing_expires_at']) < time());
    } catch (\Throwable $e) {
      $expired = true;
    }

    if (empty($row['pairing_nonce']) || $expired || empty($row['pin_code'])) {
      $pin = random_code();
      $pnonce = random_nonce();
      $pexp = add_minutes_ts(10);

      $stmt = $db->prepare("
        UPDATE tahta_boards
        SET pin_code=?, pairing_nonce=?, pairing_expires_at=?, updated_at=?
        WHERE board_id=? AND owner_user_id IS NULL
      ");
      if ($stmt) {
        $stmt->bind_param('sssss', $pin, $pnonce, $pexp, $now, $boardId);
        $stmt->execute();
        $stmt->close();
      }
      $row = fetch_board($db, $boardId) ?: $row;
    }
    return $row;
  }

  // 4) Sahipli + kilitliyse unlock alanlarını üret (QR unlock için)
  if ((int)$row['is_frozen'] === 1) {
    $uExpired = true;
    if (!empty($row['unlock_expires_at'])) {
      $uExpired = (strtotime((string)$row['unlock_expires_at']) < time());
    }

    if (empty($row['unlock_code']) || empty($row['unlock_nonce']) || $uExpired) {
      $ucode = random_code();      // 6 haneli
      $unonce = random_nonce();    // 32 hex
      $uexp = add_minutes_ts(5);   // 5 dk

      $stmt = $db->prepare("
        UPDATE tahta_boards
        SET unlock_code=?, unlock_nonce=?, unlock_expires_at=?, updated_at=?
        WHERE board_id=?
      ");
      if ($stmt) {
        $stmt->bind_param('sssss', $ucode, $unonce, $uexp, $now, $boardId);
        $stmt->execute();
        $stmt->close();
      }
      $row = fetch_board($db, $boardId) ?: $row;
    }
  } else {
    // açıkken unlock alanlarını temizlemek istersen:
    // (opsiyonel) burada bırakıyoruz; istersen clear edebiliriz.
  }

  return $row;
}

function is_board_cookie_for(string $boardId): bool {
  [$cb, $ck] = get_board_cookie();
  if ($cb === '' || $ck === '') return false;
  return hash_equals($cb, $boardId);
}

function is_owner_of_board(array $row, int $uid): bool {
  // Hem owner_user_id hem de paired_token_hash ile uyumlu kalsın
  if (!empty($row['owner_user_id']) && (int)$row['owner_user_id'] === $uid) return true;
  if (!empty($row['paired_token_hash'])) {
    return hash_equals((string)$row['paired_token_hash'], user_token_hash($uid));
  }
  return false;
}

/* =========================
   ROUTER
========================= */
try {

  /* ===== BOARD BOOT (tahta cihazı çağırır) ===== */
  if ($action === 'board_boot') {
    $boardId = trim((string)($_POST['board'] ?? ''));
    if ($boardId === '') json_out(['ok'=>false,'error'=>'board_missing'], 400);

    // Cookie board_id bu board ile aynı olmalı
    if (!is_board_cookie_for($boardId)) json_out(['ok'=>false,'error'=>'board_cookie_missing_or_mismatch'], 403);

    // cookie key raw gerekli
    [, $cookieKey] = get_board_cookie();
    if ($cookieKey === '') json_out(['ok'=>false,'error'=>'device_key_missing'], 400);

    $row = ensure_board($db, $boardId, $cookieKey);

    $owned = (!empty($row['owner_user_id']) || !empty($row['paired_at'])) ? 1 : 0;

    $out = [
      'ok' => true,
      'board' => $row['board_id'],
      'owned' => $owned,
      'is_frozen' => (int)$row['is_frozen'],
      'updated_at' => $row['updated_at'],
    ];

    if ($owned === 0) {
      $out['pairing_code'] = (string)$row['pin_code'];
      $out['pairing_nonce'] = (string)$row['pairing_nonce'];
      $out['pairing_expires_at'] = (string)$row['pairing_expires_at'];
    } else {
      // Sahipli + kilitliyse QR unlock üretildi
      if ((int)$row['is_frozen'] === 1) {
        $out['unlock_code'] = (string)($row['unlock_code'] ?? '');
        $out['unlock_nonce'] = (string)($row['unlock_nonce'] ?? '');
        $out['unlock_expires_at'] = (string)($row['unlock_expires_at'] ?? '');
      } else {
        $out['unlock_code'] = '';
        $out['unlock_nonce'] = '';
        $out['unlock_expires_at'] = '';
      }
    }

    json_out($out);
  }

  /* ===== MY BOARDS (çoklu tahta) ===== */
  if ($action === 'my_boards') {
    $uid = require_uid();
    $uth = user_token_hash($uid);

    $sql = "
      SELECT board_id
      FROM tahta_boards
      WHERE (owner_user_id=?)
         OR (paired_token_hash=? AND paired_at IS NOT NULL)
      ORDER BY updated_at DESC
    ";
    $stmt = $db->prepare($sql);
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('is', $uid, $uth);
    $stmt->execute();
    $stmt->bind_result($bid);

    $boards = [];
    while ($stmt->fetch()) {
      $boards[] = (string)$bid;
    }
    $stmt->close();

    json_out(['ok'=>true,'boards'=>$boards]);
  }

  /* ===== MY BOARD (eski uyumluluk / tek) ===== */
  if ($action === 'my_board') {
    $uid = require_uid();
    $uth = user_token_hash($uid);

    $stmt = $db->prepare("
      SELECT board_id, is_frozen, updated_at
      FROM tahta_boards
      WHERE (owner_user_id=?)
         OR (paired_token_hash=? AND paired_at IS NOT NULL)
      ORDER BY updated_at DESC
      LIMIT 1
    ");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('is', $uid, $uth);
    $stmt->execute();
    $stmt->bind_result($bid, $frozen, $upd);

    if ($stmt->fetch()) {
      $stmt->close();
      json_out(['ok'=>true,'board'=>$bid,'is_frozen'=>(int)$frozen,'updated_at'=>$upd]);
    }
    $stmt->close();
    json_out(['ok'=>true,'board'=>null]);
  }

  /* ===== PAIR BIND (kullanıcı sahiplenir) ===== */
  if ($action === 'pair_bind') {
    $uid = require_uid();

    $boardId = trim((string)($_POST['board'] ?? ''));
    $code    = trim((string)($_POST['code'] ?? ''));
    $nonce   = trim((string)($_POST['nonce'] ?? ''));

    if ($boardId === '' || $nonce === '') json_out(['ok'=>false,'error'=>'missing'], 400);

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_not_found'], 404);

    if (!empty($row['owner_user_id']) || !empty($row['paired_at'])) {
      json_out(['ok'=>false,'error'=>'already_owned'], 409);
    }

    // nonce + expire kontrol
    if (!hash_equals((string)($row['pairing_nonce'] ?? ''), $nonce)) {
      json_out(['ok'=>false,'error'=>'nonce_invalid'], 403);
    }
    if (empty($row['pairing_expires_at']) || strtotime((string)$row['pairing_expires_at']) < time()) {
      json_out(['ok'=>false,'error'=>'pairing_expired'], 403);
    }

    // Pin zorunlu (isteğe göre config ile kapatılabilir)
    $requireCode = (int)($c['pair_require_code'] ?? 1);
    if ($requireCode === 1) {
      if ($code === '') json_out(['ok'=>false,'error'=>'code_missing'], 400);
      if (!hash_equals((string)($row['pin_code'] ?? ''), $code)) {
        json_out(['ok'=>false,'error'=>'code_invalid'], 403);
      }
    }

    $now = now_ts();
    $uth = user_token_hash($uid);

    $stmt = $db->prepare("
      UPDATE tahta_boards
      SET owner_user_id=?,
          paired_token_hash=?,
          paired_at=?,
          updated_at=?
      WHERE board_id=? AND owner_user_id IS NULL
    ");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('issss', $uid, $uth, $now, $now, $boardId);
    $stmt->execute();
    $ok = $stmt->affected_rows > 0;
    $stmt->close();

    if (!$ok) json_out(['ok'=>false,'error'=>'pair_failed'], 500);

    if (function_exists('log_action')) {
      log_action($db, $boardId, 'pair_bind', $uid);
    }

    json_out(['ok'=>true]);
  }

  /* ===== STATUS (owner ya da board) ===== */
  if ($action === 'status') {
    $boardId = trim((string)($_GET['board'] ?? ''));
    if ($boardId === '') json_out(['ok'=>false,'error'=>'board_missing'], 400);

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_not_found'], 404);

    $isBoard = is_board_cookie_for($boardId);
    $isOwner = false;
    if (!empty($_SESSION['uid'])) {
      $isOwner = is_owner_of_board($row, (int)$_SESSION['uid']);
    }
    if (!$isBoard && !$isOwner) json_out(['ok'=>false,'error'=>'unauthorized'], 403);

    json_out([
      'ok'=>true,
      'is_frozen'=>(int)$row['is_frozen'],
      'owned'=> (!empty($row['owner_user_id']) || !empty($row['paired_at'])) ? 1 : 0,
      'updated_at'=>$row['updated_at'],
    ]);
  }

  /* ===== FREEZE (owner ya da board) ===== */
  if ($action === 'freeze') {
    $boardId = trim((string)($_POST['board'] ?? ''));
    if ($boardId === '') json_out(['ok'=>false,'error'=>'board_missing'], 400);

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_not_found'], 404);

    $isBoard = is_board_cookie_for($boardId);
    $isOwner = (!empty($_SESSION['uid'])) ? is_owner_of_board($row, (int)$_SESSION['uid']) : false;

    if (!$isBoard && !$isOwner) json_out(['ok'=>false,'error'=>'unauthorized'], 403);

    $now = now_ts();
    $stmt = $db->prepare("UPDATE tahta_boards SET is_frozen=1, updated_at=? WHERE board_id=?");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('ss', $now, $boardId);
    $stmt->execute();
    $stmt->close();

    if (function_exists('log_action')) {
      log_action($db, $boardId, 'freeze', !empty($_SESSION['uid']) ? (int)$_SESSION['uid'] : null);
    }

    json_out(['ok'=>true,'is_frozen'=>1]);
  }

  /* ===== UNFREEZE (owner) ===== */
  if ($action === 'unfreeze') {
    $uid = require_uid();

    $boardId = trim((string)($_POST['board'] ?? ''));
    if ($boardId === '') json_out(['ok'=>false,'error'=>'board_missing'], 400);

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_not_found'], 404);

    if (!is_owner_of_board($row, $uid)) json_out(['ok'=>false,'error'=>'unauthorized'], 403);

    // strict unlock: QR kod doğrulaması
    $strict = (int)($c['unlock_strict'] ?? 0);

    if ($strict === 1) {
      $unlock_code = trim((string)($_POST['unlock_code'] ?? ''));
      $nonce       = trim((string)($_POST['nonce'] ?? ''));

      if ($unlock_code === '') json_out(['ok'=>false,'error'=>'unlock_code_missing'], 400);

      // db'deki unlock alanları dolu olmalı ve expire olmamalı
      if (empty($row['unlock_code']) || empty($row['unlock_expires_at'])) {
        json_out(['ok'=>false,'error'=>'unlock_not_ready'], 409);
      }
      if (strtotime((string)$row['unlock_expires_at']) < time()) {
        json_out(['ok'=>false,'error'=>'unlock_expired'], 403);
      }
      if (!hash_equals((string)$row['unlock_code'], $unlock_code)) {
        json_out(['ok'=>false,'error'=>'unlock_code_invalid'], 403);
      }
      if (!empty($row['unlock_nonce'])) {
        if ($nonce === '' || !hash_equals((string)$row['unlock_nonce'], $nonce)) {
          json_out(['ok'=>false,'error'=>'unlock_nonce_invalid'], 403);
        }
      }
    }

    $now = now_ts();
    $stmt = $db->prepare("
      UPDATE tahta_boards
      SET is_frozen=0,
          updated_at=?,
          unlock_code=NULL,
          unlock_nonce=NULL,
          unlock_expires_at=NULL
      WHERE board_id=?
    ");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('ss', $now, $boardId);
    $stmt->execute();
    $stmt->close();

    if (function_exists('log_action')) {
      log_action($db, $boardId, 'unfreeze', $uid);
    }

    json_out(['ok'=>true,'is_frozen'=>0,'strict'=>$strict]);
  }

  /* ===== PAIR REFRESH (board cihazı) ===== */
  if ($action === 'pair_refresh') {
    $boardId = trim((string)($_POST['board'] ?? ''));
    if ($boardId === '') json_out(['ok'=>false,'error'=>'board_missing'], 400);
    if (!is_board_cookie_for($boardId)) json_out(['ok'=>false,'error'=>'board_unauthorized'], 403);

    $row = fetch_board($db, $boardId);
    if (!$row) json_out(['ok'=>false,'error'=>'board_not_found'], 404);
    if (!empty($row['owner_user_id']) || !empty($row['paired_at'])) json_out(['ok'=>false,'error'=>'already_owned'], 409);

    $now = now_ts();
    $pin = random_code();
    $pnonce = random_nonce();
    $pexp = add_minutes_ts(10);

    $stmt = $db->prepare("
      UPDATE tahta_boards
      SET pin_code=?, pairing_nonce=?, pairing_expires_at=?, updated_at=?
      WHERE board_id=? AND owner_user_id IS NULL
    ");
    if (!$stmt) json_out(['ok'=>false,'error'=>'db_prepare_failed'], 500);
    $stmt->bind_param('sssss', $pin, $pnonce, $pexp, $now, $boardId);
    $stmt->execute();
    $stmt->close();

    json_out([
      'ok'=>true,
      'pairing_code'=>$pin,
      'pairing_nonce'=>$pnonce,
      'pairing_expires_at'=>$pexp,
    ]);
  }

  json_out(['ok'=>false,'error'=>'unknown_action','action'=>$action], 400);

} catch (Throwable $e) {
  json_out([
    'ok'=>false,
    'error'=>'fatal',
    'fatal'=>[
      'message'=>$e->getMessage(),
      'file'=>$e->getFile(),
      'line'=>$e->getLine(),
    ],
  ], 500);
}
