<?php
// ============================================================
//  config.php  —  Edit DB_PASS if you set a MySQL password
// ============================================================
define('DB_HOST', 'localhost');
define('DB_NAME', 'wachat');
define('DB_USER', 'root');
define('DB_PASS', '');
define('JWT_KEY',  'WaChat2024SecretKey_ChangeMe!');
define('JWT_TTL',  86400 * 30);
define('UP_DIR',   __DIR__ . '/../uploads/');
define('UP_URL',   'uploads/');
define('MAX_MB',   16);

// ── GIF search provider ──────────────────────────────────────
// GIF search needs an API key — there is no free keyless GIF API anymore
// (GIPHY went paid, Google is retiring Tenor's developer API). WhatsApp now uses
// KLIPY, which has a lifetime free tier. Pick a provider and paste a free key:
//   'klipy' → get a key at https://klipy.com/developers   (recommended; WhatsApp uses this)
//   'tenor' → get a key at https://developers.google.com/tenor (Google Cloud key)
//   'giphy' → get a key at https://developers.giphy.com (now a paid product)
// Leave GIF_API_KEY empty to disable GIF search (the picker shows a hint).
define('GIF_PROVIDER', 'klipy');
define('GIF_API_KEY',  'i1eVIdEoOuhiODa4YHZqw8MwaWTtidbnH1KfoEWrPohuAkoLvAIFw3YQY3pKoqyi');

// Send JSON headers — only once, no ob_start needed
if (!headers_sent()) {
    header('Content-Type: application/json; charset=utf-8');
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: Content-Type, Authorization');
    header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
}
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// ── Database connection ──────────────────────────────────────
function db() {
    static $c = null;
    if ($c) return $c;
    try {
        $c = new PDO(
            'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4',
            DB_USER, DB_PASS,
            array(
                PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES   => false
            )
        );
    } catch (PDOException $e) {
        http_response_code(500);
        die(json_encode(array('error' => 'Cannot connect to MySQL: ' . $e->getMessage() .
            ' — Make sure MySQL is running in XAMPP and database "wachat" exists.')));
    }
    return $c;
}

// ── Lightweight runtime schema upgrades ─────────────────────
// Keeps older installs working without forcing a manual SQL import.
function ensure_app_schema() {
    static $done = false;
    if ($done) return;
    $done = true;
    $db = db();
    $alter = array(
        "ALTER TABLE users ADD COLUMN display_name VARCHAR(80) NOT NULL DEFAULT '' AFTER username",
        "ALTER TABLE users ADD COLUMN key_backup MEDIUMTEXT NULL AFTER public_key",
        "ALTER TABLE users ADD COLUMN key_backup_iv VARCHAR(64) NOT NULL DEFAULT '' AFTER key_backup",
        "ALTER TABLE users ADD COLUMN timezone VARCHAR(64) NOT NULL DEFAULT '' AFTER about",
        "ALTER TABLE users ADD COLUMN country VARCHAR(8) NOT NULL DEFAULT '' AFTER timezone",
        "ALTER TABLE users ADD COLUMN chat_wallpaper VARCHAR(255) NOT NULL DEFAULT '' AFTER country",
        "ALTER TABLE presence ADD COLUMN timezone VARCHAR(64) NOT NULL DEFAULT '' AFTER last_seen",
        "ALTER TABLE presence ADD COLUMN country VARCHAR(8) NOT NULL DEFAULT '' AFTER timezone",
        "ALTER TABLE presence ADD COLUMN typing_at DATETIME NULL AFTER typing_to",
        "ALTER TABLE messages ADD COLUMN status_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP AFTER status"
    );
    foreach ($alter as $sql) {
        try { $db->exec($sql); } catch (Exception $e) {}
    }
    try {
        $db->exec("CREATE TABLE IF NOT EXISTS protected_chats (
            owner     VARCHAR(50) NOT NULL,
            peer      VARCHAR(50) NOT NULL,
            locked_at DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (owner, peer),
            INDEX idx_protected_owner (owner)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    } catch (Exception $e) {}
    try {
        $db->exec("CREATE TABLE IF NOT EXISTS call_sessions (
            id            BIGINT AUTO_INCREMENT PRIMARY KEY,
            caller        VARCHAR(50) NOT NULL,
            receiver      VARCHAR(50) NOT NULL,
            call_type     ENUM('voice','video') NOT NULL DEFAULT 'voice',
            status        ENUM('ringing','accepted','declined','ended') NOT NULL DEFAULT 'ringing',
            offer_sdp     MEDIUMTEXT NULL,
            answer_sdp    MEDIUMTEXT NULL,
            caller_ice    MEDIUMTEXT NULL,
            receiver_ice  MEDIUMTEXT NULL,
            created_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            ended_at      DATETIME NULL,
            INDEX idx_call_receiver_status (receiver, status, updated_at),
            INDEX idx_call_caller (caller, updated_at),
            INDEX idx_call_receiver (receiver, updated_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    } catch (Exception $e) {}
    try {
        $db->exec("UPDATE users SET display_name=username WHERE display_name=''");
    } catch (Exception $e) {}
    try {
        $db->exec("UPDATE messages SET status_at=COALESCE(status_at, sent_at, UTC_TIMESTAMP()) WHERE status_at IS NULL");
    } catch (Exception $e) {}
}

function touch_presence($username) {
    ensure_app_schema();
    // Keep online presence fresh without clearing typing state. Typing freshness is
    // handled separately with presence.typing_at so polling cannot cancel “typing…”.
    db()->prepare("INSERT INTO presence (username, last_seen, is_typing, typing_to)
                   VALUES (?, UTC_TIMESTAMP(), 0, '')
                   ON DUPLICATE KEY UPDATE last_seen=UTC_TIMESTAMP()")
        ->execute(array($username));
}

function set_presence_location($username, $timezone, $country) {
    ensure_app_schema();
    $timezone = substr(trim((string)$timezone), 0, 64);
    $country  = strtoupper(substr(trim((string)$country), 0, 8));
    db()->prepare("UPDATE users SET timezone=?, country=? WHERE username=?")
        ->execute(array($timezone, $country, $username));
    db()->prepare("INSERT INTO presence (username, last_seen, timezone, country, is_typing, typing_to)
                   VALUES (?, UTC_TIMESTAMP(), ?, ?, 0, '')
                   ON DUPLICATE KEY UPDATE last_seen=UTC_TIMESTAMP(), timezone=VALUES(timezone), country=VALUES(country)")
        ->execute(array($username, $timezone, $country));
}

// ── Tiny JWT (no dependencies) ───────────────────────────────
function make_token($username) {
    $h   = _b64(json_encode(array('alg' => 'HS256', 'typ' => 'JWT')));
    $p   = _b64(json_encode(array('u' => $username, 'exp' => time() + JWT_TTL)));
    $sig = _b64(hash_hmac('sha256', "$h.$p", JWT_KEY, true));
    return "$h.$p.$sig";
}
function read_token($tok) {
    $parts = explode('.', (string)$tok);
    if (count($parts) !== 3) return null;
    $expected = _b64(hash_hmac('sha256', $parts[0] . '.' . $parts[1], JWT_KEY, true));
    if (!hash_equals($expected, $parts[2])) return null;
    $data = json_decode(_b64d($parts[1]), true);
    if (!$data || !isset($data['exp']) || $data['exp'] < time()) return null;
    return $data;
}
function _b64($s)  { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); }
function _b64d($s) { return base64_decode(strtr($s, '-_', '+/') . str_repeat('=', (4 - strlen($s) % 4) % 4)); }

// ── Require valid JWT, return username ───────────────────────
function must_auth() {
    $h = '';
    if (isset($_SERVER['HTTP_AUTHORIZATION']))          $h = $_SERVER['HTTP_AUTHORIZATION'];
    elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) $h = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
    elseif (isset($_GET['token']))                      $h = 'Bearer ' . $_GET['token'];

    if (strpos($h, 'Bearer ') !== 0) die_err('Not authenticated', 401);
    $d = read_token(substr($h, 7));
    if (!$d) die_err('Token invalid or expired', 401);

    // Refresh presence in UTC while preserving detected timezone/country.
    try { touch_presence($d['u']); } catch (Exception $e) {}

    return $d['u'];
}

// ── Response helpers ─────────────────────────────────────────
function die_ok($data = array()) {
    $out = json_encode(array_merge(array('ok' => true), $data));
    die($out);
}
function die_err($msg, $code = 400) {
    http_response_code($code);
    die(json_encode(array('error' => $msg)));
}

// Short aliases
function ok($d = array())     { die_ok($d); }
function fail($m, $c = 400)   { die_err($m, $c); }

// Cloudflare Calls / Realtime TURN
define('CLOUDFLARE_TURN_KEY_ID', '7dfb37af422a7de89691e77de79273e1');
define('CLOUDFLARE_TURN_API_TOKEN', 'a4be9e4064bc2a9a7c6a601d6af3956c76f26fe00ba9c54948cf5225ae2dfcc3');

// 86400 = 24 hours
define('CLOUDFLARE_TURN_TTL', 86400);