<?php
require_once __DIR__ . '/config.php';
$me = must_auth();
ensure_app_schema();
expire_stale_ringing_calls($me);

function call_row_for_user($id, $me) {
    $st = db()->prepare("SELECT *, DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at_iso,
                                DATE_FORMAT(updated_at,'%Y-%m-%dT%H:%i:%sZ') AS updated_at_iso
                         FROM call_sessions
                         WHERE id=? AND (caller=? OR receiver=?) LIMIT 1");
    $st->execute(array((int)$id, $me, $me));
    return $st->fetch();
}
function decode_json_array($s) {
    $a = json_decode((string)$s, true);
    return is_array($a) ? $a : array();
}

function expire_stale_ringing_calls($me) {
    // A browser refresh, denied camera permission, or mobile tab suspension can leave
    // a ringing row behind. Expire only unanswered calls; accepted calls are not
    // touched because a long WebRTC call may not keep updating this table.
    db()->prepare("UPDATE call_sessions
                   SET status='ended', ended_at=COALESCE(ended_at, UTC_TIMESTAMP()), updated_at=UTC_TIMESTAMP()
                   WHERE status='ringing'
                     AND updated_at<DATE_SUB(UTC_TIMESTAMP(), INTERVAL 90 SECOND)
                     AND (caller=? OR receiver=?)")
        ->execute(array($me, $me));
}

function public_call($r, $me) {
    if (!$r) return null;
    $side = ($r['caller'] === $me) ? 'caller' : 'receiver';
    $remoteIce = ($side === 'caller') ? decode_json_array($r['receiver_ice']) : decode_json_array($r['caller_ice']);
    return array(
        'id' => (int)$r['id'],
        'caller' => $r['caller'],
        'receiver' => $r['receiver'],
        'peer' => ($side === 'caller' ? $r['receiver'] : $r['caller']),
        'side' => $side,
        'type' => $r['call_type'],
        'status' => $r['status'],
        'offer' => $r['offer_sdp'],
        'answer' => $r['answer_sdp'],
        'remoteIce' => $remoteIce,
        'createdAt' => $r['created_at_iso'],
        'updatedAt' => $r['updated_at_iso']
    );
}

$method = $_SERVER['REQUEST_METHOD'];
$action = isset($_GET['action']) ? $_GET['action'] : '';

if ($method === 'GET') {
    if ($action === 'incoming') {
        $st = db()->prepare("SELECT *, DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at_iso,
                                    DATE_FORMAT(updated_at,'%Y-%m-%dT%H:%i:%sZ') AS updated_at_iso
                             FROM call_sessions
                             WHERE receiver=? AND status='ringing'
                               AND updated_at>=DATE_SUB(UTC_TIMESTAMP(), INTERVAL 75 SECOND)
                             ORDER BY id DESC LIMIT 1");
        $st->execute(array($me));
        ok(array('call' => public_call($st->fetch(), $me)));
    }
    if ($action === 'status') {
        $id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
        if (!$id) fail('Missing call id');
        ok(array('call' => public_call(call_row_for_user($id, $me), $me)));
    }
    fail('Unknown action', 400);
}

if ($method !== 'POST') fail('Method not allowed', 405);
$b = json_decode(file_get_contents('php://input'), true);
if (!is_array($b)) fail('Invalid JSON');
$action = isset($b['action']) ? $b['action'] : $action;

if ($action === 'start') {
    $to = isset($b['to']) ? trim($b['to']) : '';
    $type = isset($b['type']) && $b['type'] === 'video' ? 'video' : 'voice';
    $offer = isset($b['offer']) ? trim($b['offer']) : '';
    if ($to === '' || $to === $me) fail('Invalid call recipient');
    if ($offer === '') fail('Missing call offer');
    $st = db()->prepare("SELECT id FROM users WHERE username=? LIMIT 1");
    $st->execute(array($to));
    if (!$st->fetch()) fail('Recipient not found', 404);
    db()->prepare("INSERT INTO call_sessions (caller, receiver, call_type, status, offer_sdp, created_at, updated_at)
                   VALUES (?, ?, ?, 'ringing', ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())")
        ->execute(array($me, $to, $type, $offer));
    $id = db()->lastInsertId();
    ok(array('call' => public_call(call_row_for_user($id, $me), $me)));
}

$id = isset($b['callId']) ? (int)$b['callId'] : 0;
if (!$id) fail('Missing call id');
$row = call_row_for_user($id, $me);
if (!$row) fail('Call not found', 404);
$side = ($row['caller'] === $me) ? 'caller' : 'receiver';

if ($action === 'accept') {
    if ($side !== 'receiver') fail('Only the receiver can accept this call', 403);
    $answer = isset($b['answer']) ? trim($b['answer']) : '';
    if ($answer === '') fail('Missing call answer');
    db()->prepare("UPDATE call_sessions SET status='accepted', answer_sdp=?, updated_at=UTC_TIMESTAMP() WHERE id=? AND receiver=?")
        ->execute(array($answer, $id, $me));
    ok(array('call' => public_call(call_row_for_user($id, $me), $me)));
}

if ($action === 'ice') {
    $candidate = isset($b['candidate']) ? $b['candidate'] : null;
    if (!$candidate) fail('Missing ICE candidate');
    $col = ($side === 'caller') ? 'caller_ice' : 'receiver_ice';
    $arr = decode_json_array($row[$col]);
    if (count($arr) < 300) $arr[] = $candidate;
    db()->prepare("UPDATE call_sessions SET $col=?, updated_at=UTC_TIMESTAMP() WHERE id=? AND (caller=? OR receiver=?)")
        ->execute(array(json_encode($arr), $id, $me, $me));
    ok();
}

if ($action === 'end' || $action === 'decline') {
    $status = ($action === 'decline') ? 'declined' : 'ended';
    db()->prepare("UPDATE call_sessions SET status=?, ended_at=UTC_TIMESTAMP(), updated_at=UTC_TIMESTAMP() WHERE id=? AND (caller=? OR receiver=?)")
        ->execute(array($status, $id, $me, $me));
    ok(array('call' => public_call(call_row_for_user($id, $me), $me)));
}

fail('Unknown action', 400);
