<?php
declare(strict_types=1);

require_once __DIR__ . '/_cors.php';
header('Content-Type: application/json; charset=utf-8');

function respond(int $status, array $payload): void {
  http_response_code($status);
  echo json_encode($payload, JSON_UNESCAPED_SLASHES);
  exit;
}

function pickSessionName(): ?string {
  if (!empty($_COOKIE['PHPSESSADM'])) return 'PHPSESSADM';
  if (!empty($_COOKIE['PHPSESSCLI'])) return 'PHPSESSCLI';
  if (!empty($_COOKIE['PHPSESSAFF'])) return 'PHPSESSAFF';
  return null;
}

require_once __DIR__ . '/conn.php';
require_once __DIR__ . '/../config/pusher.php';

$sessionName = pickSessionName();
if (!$sessionName) respond(401, ['error' => 'Not authenticated (no valid session cookie)']);

session_name($sessionName);
session_start();

$userId = (int)($_SESSION['user_id'] ?? 0);
if ($userId <= 0) $userId = (int)($_SESSION['SESS_MEMBER_ID'] ?? 0);
if ($userId <= 0) respond(401, ['error' => 'Not authenticated']);

$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) respond(400, ['error' => 'Invalid JSON body']);

$roomIdRaw = (string)($data['roomId'] ?? '');
$body = trim((string)($data['body'] ?? ''));
$idempotencyKey = trim((string)($data['idempotencyKey'] ?? ''));

if ($roomIdRaw === '' || !ctype_digit($roomIdRaw) || $body === '') {
  respond(400, ['error' => 'roomId (number) and body are required']);
}
if ($idempotencyKey === '') {
  respond(400, ['error' => 'idempotencyKey is required']);
}

$roomId = (int)$roomIdRaw;

/**
 * Load user context needed for scope authorization
 */
$userStmt = $db->prepare("
  SELECT id, role, county_id, constituency_id, ward_id, polling_station_id
  FROM users
  WHERE id = :uid
  LIMIT 1
");
$userStmt->execute([':uid' => $userId]);
$user = $userStmt->fetch(PDO::FETCH_ASSOC);
if (!$user) respond(401, ['error' => 'User not found']);

function isSuperAdmin(array $user): bool {
  // adjust to your role strings
  return in_array(strtolower((string)$user['role']), ['super_admin', 'superadmin'], true);
}
function isNational(array $user): bool {
  return in_array(strtolower((string)$user['role']), ['national', 'national_leader'], true);
}
function isCounty(array $user): bool {
  return in_array(strtolower((string)$user['role']), ['county', 'county_rep'], true);
}
function isConstituency(array $user): bool {
  return in_array(strtolower((string)$user['role']), ['constituency', 'constituency_rep'], true);
}
function isWard(array $user): bool {
  return in_array(strtolower((string)$user['role']), ['ward', 'ward_rep'], true);
}

/**
 * Membership-required check for DM/custom rooms
 */
function isMember(PDO $db, int $roomId, int $userId): bool {
  $st = $db->prepare("
    SELECT 1
    FROM pth_party_room_members
    WHERE room_id = :rid AND user_id = :uid
    LIMIT 1
  ");
  $st->execute([':rid' => $roomId, ':uid' => $userId]);
  return (bool)$st->fetchColumn();
}

/**
 * Scope access check (no membership rows needed)
 */
function canAccessScopeRoom(array $user, array $room): bool {
  if (isSuperAdmin($user)) return true;

  $scope = (string)$room['scope_level'];

  // national rooms: national + above can access; others can access if your policy allows.
  if ($scope === 'national') {
    // If you want EVERYONE in the party to access national rooms, return true here.
    // If you want only leadership, restrict.
    return true;
  }

  // Below assumes user has geo IDs set properly.
  $uCounty = (int)($user['county_id'] ?? 0);
  $uConst  = (int)($user['constituency_id'] ?? 0);
  $uWard   = (int)($user['ward_id'] ?? 0);
  $uPs     = (int)($user['polling_station_id'] ?? 0);

  $rCounty = (int)($room['county_id'] ?? 0);
  $rConst  = (int)($room['constituency_id'] ?? 0);
  $rWard   = (int)($room['ward_id'] ?? 0);
  $rPs     = (int)($room['polling_station_id'] ?? 0);

  // Leaders above can view/send within their scope downward:
  if (isNational($user)) return true;

  if ($scope === 'county') {
    return $uCounty > 0 && $uCounty === $rCounty;
  }
  if ($scope === 'constituency') {
    // constituency reps (and above) in same const
    if (isCounty($user)) return $uCounty > 0 && $uCounty === $rCounty; // county oversees all consts in county
    return $uConst > 0 && $uConst === $rConst;
  }
  if ($scope === 'ward') {
    if (isCounty($user)) return $uCounty > 0 && $uCounty === $rCounty;
    if (isConstituency($user)) return $uConst > 0 && $uConst === $rConst;
    return $uWard > 0 && $uWard === $rWard;
  }
  if ($scope === 'polling_station') {
    if (isCounty($user)) return $uCounty > 0 && $uCounty === $rCounty;
    if (isConstituency($user)) return $uConst > 0 && $uConst === $rConst;
    if (isWard($user)) return $uWard > 0 && $uWard === $rWard;
    return $uPs > 0 && $uPs === $rPs;
  }

  return false;
}

/**
 * Posting rules (announcement rooms)
 * - Anyone can read (via canAccessScopeRoom)
 * - Only admins/leadership can post announcements (your choice)
 */
function canPostAnnouncement(array $user, array $room): bool {
  if (isSuperAdmin($user)) return true;

  $scope = (string)$room['scope_level'];
  // Example: ward rep+ can post in ward announcements, constituency rep+ in constituency, etc.
  if ($scope === 'national') return isNational($user);
  if ($scope === 'county') return isCounty($user) || isNational($user);
  if ($scope === 'constituency') return isConstituency($user) || isCounty($user) || isNational($user);
  if ($scope === 'ward') return isWard($user) || isConstituency($user) || isCounty($user) || isNational($user);

  // polling_station announcements usually restricted to ward+ (optional)
  if ($scope === 'polling_station') return isWard($user) || isConstituency($user) || isCounty($user) || isNational($user);

  return false;
}

try {
  $db->beginTransaction();

  // Load room
  $roomStmt = $db->prepare("
    SELECT id, room_type, room_key, room_name, scope_level,
           county_id, constituency_id, ward_id, polling_station_id, party_id
    FROM pth_party_room
    WHERE id = :rid
    LIMIT 1
  ");
  $roomStmt->execute([':rid' => $roomId]);
  $room = $roomStmt->fetch(PDO::FETCH_ASSOC);

  if (!$room) {
    $db->rollBack();
    respond(404, ['error' => 'Room not found']);
  }

  $roomType = (string)$room['room_type'];
  $scopeLevel = (string)$room['scope_level'];

  // DM auto-heal (only if you keep dm:{a}_{b} format in room_key)
  if ($roomType === 'dm') {
    $rk = (string)$room['room_key'];
    if (str_starts_with($rk, 'dm:')) {
      $pair = substr($rk, 3);
      if (strpos($pair, '_') !== false) {
        [$aStr, $bStr] = explode('_', $pair, 2);
        $a = (int)$aStr; $b = (int)$bStr;

        if ($a > 0 && $b > 0) {
          $ins = $db->prepare("
            INSERT IGNORE INTO pth_party_room_members (room_id, user_id, role)
            VALUES (:rid, :uid, 'member')
          ");
          $ins->execute([':rid' => $roomId, ':uid' => $a]);
          $ins->execute([':rid' => $roomId, ':uid' => $b]);
        }
      }
    }
  }

  // AUTHZ: can user send to this room?
  $allowed = false;

  if ($roomType === 'dm' || $scopeLevel === 'custom') {
    $allowed = isMember($db, $roomId, $userId);
  } else {
    // scope room
    $allowed = canAccessScopeRoom($user, $room);
    if ($allowed && $roomType === 'announcement') {
      $allowed = canPostAnnouncement($user, $room);
    }
  }

  if (!$allowed) {
    $db->rollBack();
    respond(403, ['error' => 'Not allowed']);
  }

  // IDEMPOTENCY (recommended: key + user + room)
  $idem = $db->prepare("
    SELECT message_id
    FROM pth_party_idempotency
    WHERE idem_key = :k AND user_id = :uid AND room_id = :rid
    LIMIT 1
  ");
  $idem->execute([':k' => $idempotencyKey, ':uid' => $userId, ':rid' => $roomId]);
  $existingMessageId = (int)($idem->fetchColumn() ?: 0);

  if ($existingMessageId > 0) {
    $msg = $db->prepare("
      SELECT id, room_id AS roomId, user_id AS senderId, body, created_at AS createdAt
      FROM pth_party_messages
      WHERE id = :id
      LIMIT 1
    ");
    $msg->execute([':id' => $existingMessageId]);
    $row = $msg->fetch(PDO::FETCH_ASSOC);

    $db->commit();
    respond(200, ['ok' => true, 'message' => $row ?: null]);
  }

  // Sender display name
  $senderName = (string)($user['name'] ?? 'User');

  // Insert message
  $insert = $db->prepare("
    INSERT INTO pth_party_messages (room_id, user_id, sender_name, body, created_at)
    VALUES (:rid, :uid, :sname, :body, NOW())
  ");
  $insert->execute([
    ':rid' => $roomId,
    ':uid' => $userId,
    ':sname' => $senderName,
    ':body' => $body,
  ]);
  $messageId = (int)$db->lastInsertId();

  // Store idempotency
  $idemIns = $db->prepare("
    INSERT INTO pth_party_idempotency (idem_key, user_id, room_id, message_id, created_at)
    VALUES (:k, :uid, :rid, :mid, NOW())
  ");
  $idemIns->execute([
    ':k' => $idempotencyKey,
    ':uid' => $userId,
    ':rid' => $roomId,
    ':mid' => $messageId,
  ]);

  // Load inserted message
  $msg = $db->prepare("
    SELECT id, room_id AS roomId, user_id AS senderId, sender_name AS senderName, body, created_at AS createdAt
    FROM pth_party_messages
    WHERE id = :id
    LIMIT 1
  ");
  $msg->execute([':id' => $messageId]);
  $row = $msg->fetch(PDO::FETCH_ASSOC);

  $db->commit();

  if (!$row) respond(500, ['error' => 'Failed to load inserted message']);

  $message = [
    'id' => (string)$row['id'],
    'roomId' => (string)$row['roomId'],
    'senderId' => (string)$row['senderId'],
    'senderName' => $row['senderName'],
    'body' => $row['body'],
    'createdAt' => $row['createdAt'],
  ];

} catch (Throwable $e) {
  if (isset($db) && $db->inTransaction()) $db->rollBack();
  respond(500, ['error' => 'Failed to send message', 'message' => $e->getMessage()]);
}

// Broadcast (keep your convention, just standardize)
$pusher = pusher_instance();
$pusher->trigger("private-room.$roomId", "message.created", [
  'roomId' => (string)$roomId,
  'message' => $message,
]);

respond(200, ['ok' => true, 'message' => $message]);
