<?php
declare(strict_types=1);

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

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

function respond(int $status, array $payload): void {
  http_response_code($status);
  echo json_encode($payload, JSON_UNESCAPED_SLASHES);
  exit;
}
function cookieNames(): array { return array_values(array_keys($_COOKIE ?? [])); }
function pickSessionName(): ?string {
  if (!empty($_COOKIE['PHPSESSADM'])) return 'PHPSESSADM';
  if (!empty($_COOKIE['PHPSESSCLI'])) return 'PHPSESSCLI';
  if (!empty($_COOKIE['PHPSESSAFF'])) return 'PHPSESSAFF';
  return null;
}

/** ---------- Role helpers (adjust role strings to match your DB) ---------- */
function roleIs(array $user, array $roles): bool {
  $r = strtolower((string)($user['role'] ?? ''));
  foreach ($roles as $x) {
    if ($r === strtolower($x)) return true;
  }
  return false;
}
function isSuperAdmin(array $user): bool { return roleIs($user, ['super_admin','superadmin']); }
function isNational(array $user): bool { return roleIs($user, ['national','national_leader']); }
function isCounty(array $user): bool { return roleIs($user, ['county','county_rep']); }
function isConstituency(array $user): bool { return roleIs($user, ['constituency','constituency_rep']); }
function isWard(array $user): bool { return roleIs($user, ['ward','ward_rep']); }

/** ---------- Membership & scope authorization ---------- */
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 for hierarchy rooms:
 * polling_station/ward/constituency/county/national
 * (no room_members rows needed for these)
 */
function canAccessScopeRoom(array $user, array $room): bool {
  if (isSuperAdmin($user)) return true;
  if (isNational($user)) return true;

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

  // POLICY: if you want everyone to read/write in national ops/group rooms, keep true.
  // If you want leadership-only, change to: return false;
  if ($scope === 'national') return true;

  $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);

  if ($scope === 'county') {
    return $uCounty > 0 && $uCounty === $rCounty;
  }

  if ($scope === 'constituency') {
    // county leaders can access all constituencies in their county
    if (isCounty($user)) return $uCounty > 0 && $uCounty === $rCounty;
    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 for announcements */
function canPostAnnouncement(array $user, array $room): bool {
  if (isSuperAdmin($user)) return true;

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

  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);
  if ($scope === 'polling_station') return isWard($user) || isConstituency($user) || isCounty($user) || isNational($user);

  return false;
}

/** ========== SESSION ========== */
$sessionName = pickSessionName();
if (!$sessionName) respond(401, ['error'=>'Not authenticated', 'cookie_names'=>cookieNames()]);

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']);

/** ========== INPUT (multipart/form-data) ========== */
$roomIdRaw = (string)($_POST['roomId'] ?? '');
$body = trim((string)($_POST['body'] ?? ''));
$idempotencyKey = trim((string)($_POST['idempotencyKey'] ?? ''));

if ($roomIdRaw === '' || !ctype_digit($roomIdRaw)) respond(400, ['error'=>'roomId is required']);
if ($idempotencyKey === '') respond(400, ['error'=>'idempotencyKey is required']);

$fileProvided = isset($_FILES['file']) && is_array($_FILES['file']) && ($_FILES['file']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE;
if ($body === '' && !$fileProvided) respond(400, ['error'=>'Provide body or attach a file']);

$roomId = (int)$roomIdRaw;

/** Load room FIRST (so DM auto-heal works) */
$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) respond(404, ['error'=>'Room not found']);

/** Load user context */
$userStmt = $db->prepare("
  SELECT id, role, name, 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']);

/** Auto-heal DM membership BEFORE authorization */
if (($room['room_type'] ?? '') === '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) {
        // Optional: only heal if both users exist
        $u1 = $db->prepare("SELECT 1 FROM users WHERE id=:id LIMIT 1");
        $u2 = $db->prepare("SELECT 1 FROM users WHERE id=:id LIMIT 1");
        $u1->execute([':id'=>$a]);
        $u2->execute([':id'=>$b]);

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

/** Authorization */
$roomType = (string)($room['room_type'] ?? '');
$scopeLevel = (string)($room['scope_level'] ?? '');

$allowed = false;
if ($roomType === 'dm' || $scopeLevel === 'custom') {
  $allowed = isMember($db, $roomId, $userId);
} else {
  $allowed = canAccessScopeRoom($user, $room);
  if ($allowed && $roomType === 'announcement') {
    $allowed = canPostAnnouncement($user, $room);
  }
}
if (!$allowed) respond(403, ['error'=>'Not allowed']);

/** File upload configuration */
$maxBytes = 25 * 1024 * 1024; // 25MB
$allowedMimes = [
  'image/png' => 'png',
  'image/jpeg' => 'jpg',
  'image/webp' => 'webp',
  'application/pdf' => 'pdf',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
  'application/msword' => 'doc',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
  'application/vnd.ms-excel' => 'xls',
  'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
  'application/vnd.ms-powerpoint' => 'ppt',
  'text/plain' => 'txt',
];

try {
  $db->beginTransaction();

  /** 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]);
  $existingId = (int)($idem->fetchColumn() ?: 0);

  if ($existingId > 0) {
    $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'=>$existingId]);
    $row = $msg->fetch(PDO::FETCH_ASSOC);

    // Load attachment (if any)
    $att = $db->prepare("
      SELECT id, file_name AS fileName, mime_type AS mimeType, file_size AS fileSize
      FROM pth_party_message_attachments
      WHERE message_id = :mid AND is_deleted = 0
      ORDER BY id ASC
      LIMIT 1
    ");
    $att->execute([':mid'=>$existingId]);
    $attachment = $att->fetch(PDO::FETCH_ASSOC) ?: null;

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

  /** Insert message */
  $senderName = (string)($user['name'] ?? 'User');

  $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
  ]);

  /** Handle file upload (optional) */
  $attachment = null;

  if ($fileProvided) {
    $f = $_FILES['file'];

    if (($f['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
      throw new RuntimeException('Upload failed with error code: ' . (string)$f['error']);
    }

    if ((int)($f['size'] ?? 0) > $maxBytes) {
      throw new RuntimeException('File too large (max 25MB)');
    }

    $origName = (string)($f['name'] ?? 'file');
    $tmpPath  = (string)($f['tmp_name'] ?? '');

    if ($tmpPath === '' || !is_file($tmpPath)) {
      throw new RuntimeException('Invalid upload temp file');
    }

    // Detect mime safely
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime  = (string)$finfo->file($tmpPath);

    if (!isset($allowedMimes[$mime])) {
      throw new RuntimeException('File type not allowed: ' . $mime);
    }

    $ext = $allowedMimes[$mime];

    // Storage (store RELATIVE path in DB, not absolute)
    $baseDir = __DIR__ . '/../uploads/chat';
    $roomDir = $baseDir . '/' . $roomId;

    if (!is_dir($roomDir) && !mkdir($roomDir, 0775, true)) {
      throw new RuntimeException('Failed to create upload directory');
    }

    $storedName = bin2hex(random_bytes(16)) . '.' . $ext;
    $destPath = $roomDir . '/' . $storedName;

    if (!move_uploaded_file($tmpPath, $destPath)) {
      throw new RuntimeException('Failed to store uploaded file');
    }

    $publicPath = "uploads/chat/$roomId/$storedName"; // serve via protected download endpoint

    $insA = $db->prepare("
      INSERT INTO pth_party_message_attachments
        (message_id, room_id, uploader_id, file_name, stored_name, mime_type, file_size, storage_path, created_at)
      VALUES
        (:mid, :rid, :uid, :fname, :sname, :mime, :size, :path, NOW())
    ");
    $insA->execute([
      ':mid'=>$messageId,
      ':rid'=>$roomId,
      ':uid'=>$userId,
      ':fname'=>$origName,
      ':sname'=>$storedName,
      ':mime'=>$mime,
      ':size'=>(int)$f['size'],
      ':path'=>$publicPath,
    ]);

    $attId = (int)$db->lastInsertId();

    $attachment = [
      'id' => (string)$attId,
      'fileName' => $origName,
      'mimeType' => $mime,
      'fileSize' => (int)$f['size'],
    ];
  }

  /** 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']);

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

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

/** PUSHER */
$pusher = pusher_instance();
$pusher->trigger("private-room.$roomId", "message.created", [
  'roomId'  => (string)$roomId,
  'message' => $responseMessage,
]);

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