<?php
/**
 * functions.php  (PHP 8.2 compatible)
 * - NO session_start()/session_destroy() here (handled by includes/bootstrap.php)
 * - No output on include
 * - Order: load table schema → define SQLiteWrapper → load database.php → build tables
 */

declare(strict_types=1);

// 0) Environment/bootstrap (sessions, base path, logging, debug toggles)
require_once __DIR__ . '/bootstrap.php';

/* -------------------------------------------------------
   1) Load table schema (provides $tables array)
------------------------------------------------------- */
require_once __DIR__ . '/table.php';

/* -------------------------------------------------------
   2) SQLite wrapper (class must exist before database.php uses it)
------------------------------------------------------- */
class SQLiteWrapper {
    private SQLite3 $db;

    public function __construct(string $dbLoc) {
        try {
            $this->db = new SQLite3($dbLoc);
        } catch (Throwable $e) {
            $this->db = new SQLite3('.db.db'); // fallback
        }
        if (!$this->db) {
            die('Error: Unable to open database.');
        }
    }

    /** [["total" => N]] */
    public function selectWithCount(string $tableName, string $countColumn, string $where = '', array $placeholders = []): array {
        $query = "SELECT COUNT($countColumn) AS total FROM $tableName";
        if ($where !== '') $query .= " WHERE $where";
        $stmt = $this->db->prepare($query);
        foreach ($placeholders as $k => $v) { $stmt->bindValue($k, $v); }
        $res = $stmt->execute();

        $out = [];
        while ($row = $res->fetchArray(SQLITE3_ASSOC)) { $out[] = $row; }
        return $out;
    }

    public function nameExists(string $tableName, string $column, string $value): bool {
        $stmt = $this->db->prepare("SELECT COUNT(*) AS count FROM $tableName WHERE $column = :value");
        $stmt->bindValue(':value', $value, SQLITE3_TEXT);
        $row = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
        return (int)($row['count'] ?? 0) > 0;
    }

    public function getRecordCount(string $tableName): string {
        $tableName = preg_replace('/[^a-zA-Z0-9_]/', '', $tableName);
        $stmt = $this->db->prepare("SELECT COUNT(*) AS count FROM $tableName");
        $row  = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
        $count = (int)($row['count'] ?? 0);
        return $count > 0 ? (string)$count : '';
    }

    public function findDuplicates(string $tableName, string $column): array {
        $stmt = $this->db->prepare("SELECT $column, COUNT(*) AS count FROM $tableName GROUP BY $column HAVING COUNT(*) > 1");
        $res  = $stmt->execute();
        $dups = [];
        while ($row = $res->fetchArray(SQLITE3_ASSOC)) { $dups[] = $row[$column]; }
        return $dups;
    }

    public function select(string $tableName, string $columns = '*', string $where = '', string $orderBy = '', array $placeholders = []): array {
        $query = "SELECT $columns FROM $tableName";
        if ($where   !== '') $query .= " WHERE $where";
        if ($orderBy !== '') $query .= " ORDER BY $orderBy";
        $stmt = $this->db->prepare($query);
        foreach ($placeholders as $k => $v) { $stmt->bindValue($k, $v); }
        $res = $stmt->execute();

        $out = [];
        while ($row = $res->fetchArray(SQLITE3_ASSOC)) { $out[] = $row; }
        return $out;
    }

    public function insert(string $tableName, array $data): bool|SQLite3Result {
        $columns      = implode(', ', array_keys($data));
        $placeholders = ':' . implode(', :', array_keys($data));
        $stmt = $this->db->prepare("INSERT INTO $tableName ($columns) VALUES ($placeholders)");
        foreach ($data as $k => $v) { $stmt->bindValue(':' . $k, $v); }
        return $stmt->execute();
    }

    public function update(string $tableName, array $data, string $where = '', array $placeholders = []): bool|SQLite3Result {
        $set = [];
        foreach ($data as $col => $val) { $set[] = "$col = :$col"; }
        $query = "UPDATE $tableName SET " . implode(', ', $set);
        if ($where !== '') $query .= " WHERE $where";

        $stmt = $this->db->prepare($query);
        foreach ($data as $k => $v)         { $stmt->bindValue(':' . $k, $v); }
        foreach ($placeholders as $k => $v) { $stmt->bindValue($k, $v); }
        return $stmt->execute();
    }

    public function delete(string $tableName, string $where = '', array $placeholders = []): bool|SQLite3Result {
        $query = "DELETE FROM $tableName";
        if ($where !== '') $query .= " WHERE $where";
        $stmt = $this->db->prepare($query);
        foreach ($placeholders as $k => $v) { $stmt->bindValue($k, $v); }
        return $stmt->execute();
    }

    public function deleteAll(string $tableName): void {
        $this->db->exec("DELETE FROM $tableName");
        $this->db->exec("DELETE FROM sqlite_sequence WHERE name='$tableName'");
    }

    public function insertIfEmpty(string $tableName, array $data): bool {
        if (!$this->isEmptyTable($tableName)) return false;
        $columns      = implode(', ', array_keys($data));
        $placeholders = ':' . implode(', :', array_keys($data));
        $stmt = $this->db->prepare("INSERT INTO $tableName ($columns) VALUES ($placeholders)");
        foreach ($data as $k => $v) { $stmt->bindValue(':' . $k, $v); }
        return (bool)$stmt->execute();
    }

    private function isEmptyTable(string $tableName): bool {
        $row = $this->db->query("SELECT COUNT(*) AS count FROM $tableName")->fetchArray(SQLITE3_ASSOC);
        return ((int)($row['count'] ?? 0)) === 0;
    }

    public function getLastInsertId(): int {
        return (int)$this->db->lastInsertRowID();
    }

    public function close(): void {
        $this->db->close();
    }
}

/* -------------------------------------------------------
   3) Database wiring (creates $db, $dbkm, $dbPath)
------------------------------------------------------- */
require_once __DIR__ . '/database.php';

/* -------------------------------------------------------
   4) Create tables if missing (idempotent)
------------------------------------------------------- */
function createTables(array $tables, string $dbLoc): void {
    try {
        $db = new SQLite3($dbLoc);
    } catch (Throwable $e) {
        $db = new SQLite3('.db.db');
    }
    if (!$db) die('Error connecting to the database');

    foreach ($tables as $tableName => $columns) {
        $sql = "CREATE TABLE IF NOT EXISTS $tableName (";
        foreach ($columns as $columnName => $columnType) {
            $sql .= "$columnName $columnType, ";
        }
        $sql = rtrim($sql, ', ') . ');';
        $db->exec($sql);
    }
    $db->close();
}
createTables($tables, $dbPath);

/* -------------------------------------------------------
   5) Helpers
------------------------------------------------------- */

function generate_token(): string {
    if (!isset($_SESSION['ftg'])) {
        $_SESSION['ftg'] = random_bytes(64);
    }
    return (string)$_SESSION['ftg'];
}

class Encryption {
    public static function run(string $input): string {
        $main = bin2hex($input);
        return strrev($main);
    }
    public static function runfake(int $length = 500): string {
        $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $out = '';
        $max = strlen($chars) - 1;
        for ($i = 0; $i < $length; $i++) {
            $out .= $chars[random_int(0, $max)];
        }
        return $out;
    }
}

class calllink {
    /**
     * @return array{result:string,data:mixed}|array{result:string,error:string}
     */
    public static function run(string $api_link): array {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $api_link,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 5,
            CURLOPT_SSL_VERIFYPEER => 0,
            CURLOPT_SSL_VERIFYHOST => 0,
        ]);
        $raw = curl_exec($ch);
        if ($raw === false) {
            return ['result' => 'error', 'error' => curl_error($ch)];
        }
        $json = json_decode((string)$raw, true);
        if ($json === null && json_last_error() !== JSON_ERROR_NONE) {
            return ['result' => 'error', 'error' => 'Invalid JSON'];
        }
        return ['result' => 'success', 'data' => $json];
    }
}

function real_ip(): string {
    $ip = $_SERVER['REMOTE_ADDR'] ?? getenv('REMOTE_ADDR') ?: 'undefined';
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = (string)$_SERVER['HTTP_X_FORWARDED_FOR'];
    } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = (string)$_SERVER['HTTP_CLIENT_IP'];
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('HTTP_CLIENT_IP')) {
        $ip = getenv('HTTP_CLIENT_IP');
    }
    return htmlspecialchars((string)$ip, ENT_QUOTES, 'UTF-8');
}

/* -------------------------------------------------------
   6) Google reCAPTCHA helpers
------------------------------------------------------- */

/**
 * Ensure a default row exists and return current settings.
 * @return array{enabled:int,site_key:string,secret_key:string}
 */
function captcha_get(SQLiteWrapper $db): array {
    // Seed default row if table empty
    $db->insertIfEmpty('captcha_settings', [
        'id'         => 1,
        'enabled'    => 0,
        'site_key'   => '',
        'secret_key' => '',
    ]);
    $rows = $db->select('captcha_settings', '*', 'id = :id', '', [':id' => 1]);
    if (!empty($rows)) {
        return [
            'enabled'    => (int)($rows[0]['enabled'] ?? 0),
            'site_key'   => (string)($rows[0]['site_key'] ?? ''),
            'secret_key' => (string)($rows[0]['secret_key'] ?? ''),
        ];
    }
    return ['enabled' => 0, 'site_key' => '', 'secret_key' => ''];
}

/** Save/update captcha settings (creates row if missing). */
function captcha_save(SQLiteWrapper $db, int $enabled, string $siteKey, string $secretKey): bool {
    $rows = $db->select('captcha_settings', 'id', 'id = :id', '', [':id' => 1]);
    $data = [
        'enabled'    => $enabled ? 1 : 0,
        'site_key'   => $siteKey,
        'secret_key' => $secretKey,
    ];
    if (!empty($rows)) {
        return (bool)$db->update('captcha_settings', $data, 'id = :id', [':id' => 1]);
    }
    $data['id'] = 1;
    return (bool)$db->insert('captcha_settings', $data);
}

/** Convenience helpers for use in templates/login page */
function captcha_enabled(SQLiteWrapper $db): bool {
    $cfg = captcha_get($db);
    return !empty($cfg['enabled']) && !empty($cfg['site_key']) && !empty($cfg['secret_key']);
}
function captcha_site_key(SQLiteWrapper $db): string {
    $cfg = captcha_get($db);
    return (string)($cfg['site_key'] ?? '');
}

/**
 * Server-side verify of Google reCAPTCHA v2
 * Returns true when verification passes (or when captcha is disabled).
 */
function captcha_verify(SQLiteWrapper $db): bool {
    $cfg = captcha_get($db);
    if (empty($cfg['enabled'])) {
        return true; // not enabled -> always pass
    }
    $response = $_POST['g-recaptcha-response'] ?? '';
    if ($response === '') {
        return false;
    }
    $secret = $cfg['secret_key'] ?? '';
    if ($secret === '') {
        return false;
    }

    $payload = http_build_query([
        'secret'   => $secret,
        'response' => $response,
        'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '',
    ]);

    // Try cURL first
    $ok = false;
    if (function_exists('curl_init')) {
        $ch = curl_init('https://www.google.com/recaptcha/api/siteverify');
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $payload,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 10,
        ]);
        $raw = curl_exec($ch);
        if ($raw !== false) {
            $json = json_decode($raw, true);
            $ok = !empty($json['success']);
        }
        curl_close($ch);
    } else {
        // Fallback to file_get_contents
        $ctx = stream_context_create([
            'http' => [
                'method'  => 'POST',
                'header'  => "Content-Type: application/x-www-form-urlencoded\r\n",
                'content' => $payload,
                'timeout' => 10,
            ]
        ]);
        $raw = @file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $ctx);
        if ($raw !== false) {
            $json = json_decode($raw, true);
            $ok = !empty($json['success']);
        }
    }

    return $ok;
}
