<?php
/**
 * AppInstallTokens — admin-created share links for the mobile APK.
 *
 * Life cycle:
 *   generate($label, $days) → 32-hex token, valid for $days days
 *   isValid($token)         → true if not expired + not revoked + tenant-matches
 *   recordDownload($token, $driver) → append audit row + bump counter
 *   revoke($id, $uid)       → kill the link
 *
 * Also exposes the canonical APK path so both the download controllers and
 * the admin page agree on where the file lives.
 */
class AppInstallTokens
{
    /* All APKs land here (server-side path, NOT web-served). The
       Install/apkAction streams the file via readfile() so nobody can
       hotlink around the token flow. */
    public const APK_DIR       = ROOT . '/app/downloads/';
    public const APK_LATEST    = 'pacific-driver-latest.apk';

    public function generate(string $label, int $days, int $uid, string $uname): array
    {
        $token   = bin2hex(random_bytes(16));   // 32 hex chars
        $expires = date('Y-m-d H:i:s', time() + max(1, $days) * 86400);
        $db = new DB();
        $r = $db->insertUpdate(
            "INSERT INTO app_install_tokens
                (tenant_id, token, label, created_by, created_by_name, expires_on)
             VALUES (:t, :tk, :lb, :uid, :un, :exp)",
            [
                ':t'   => defined('DEFAULT_TENANT_ID') ? DEFAULT_TENANT_ID : 1,
                ':tk'  => $token,
                ':lb'  => trim($label),
                ':uid' => $uid,
                ':un'  => $uname,
                ':exp' => $expires,
            ]
        );
        return ['ok' => $r['success'] > 0, 'id' => (int)$r['last_id'], 'token' => $token, 'expires_on' => $expires];
    }

    public function byToken(string $token): ?array
    {
        $db = new DB();
        $rs = $db->mySelect(
            "SELECT * FROM app_install_tokens WHERE token = :tk LIMIT 1",
            [':tk' => $token]
        );
        return $rs[0] ?? null;
    }

    public function byId(int $id): ?array
    {
        $db = new DB();
        $rs = $db->mySelect("SELECT * FROM app_install_tokens WHERE c0d = :id LIMIT 1", [':id' => $id]);
        return $rs[0] ?? null;
    }

    /** Returns 'ok'|'expired'|'revoked'|'not_found' */
    public function status(?array $row): string
    {
        if (!$row)                              return 'not_found';
        if ((int)$row['revoked'] === 1)         return 'revoked';
        if (strtotime($row['expires_on']) < time()) return 'expired';
        return 'ok';
    }

    /** Log a fresh install + bump the counter. */
    public function recordDownload(int $tokenId, array $driver): int
    {
        $db = new DB();
        $r = $db->insertUpdate(
            "INSERT INTO app_install_downloads
                (token_id, downloader_name, downloader_phone, downloader_employee_no,
                 ip_address, user_agent)
             VALUES (:tid, :n, :ph, :emp, :ip, :ua)",
            [
                ':tid' => $tokenId,
                ':n'   => trim((string)($driver['name'] ?? '')),
                ':ph'  => trim((string)($driver['phone'] ?? '')),
                ':emp' => trim((string)($driver['employee_no'] ?? '')) ?: null,
                ':ip'  => substr((string)($_SERVER['REMOTE_ADDR'] ?? ''), 0, 45),
                ':ua'  => substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255),
            ]
        );
        $db->insertUpdate(
            "UPDATE app_install_tokens
                SET downloads_count = downloads_count + 1,
                    last_download_on = NOW()
              WHERE c0d = :id",
            [':id' => $tokenId]
        );
        return (int)$r['last_id'];
    }

    public function revoke(int $id, int $uid): array
    {
        $db = new DB();
        $db->insertUpdate(
            "UPDATE app_install_tokens
                SET revoked = 1, revoked_by = :uid, revoked_on = NOW()
              WHERE c0d = :id",
            [':id' => $id, ':uid' => $uid]
        );
        return ['ok' => true, 'msg' => 'Link revoked.'];
    }

    public function listAll(int $limit = 50): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT t.*,
                    CASE
                      WHEN t.revoked = 1 THEN 'revoked'
                      WHEN t.expires_on < NOW() THEN 'expired'
                      ELSE 'active'
                    END AS state
               FROM app_install_tokens t
              WHERE t.tenant_id = :t
           ORDER BY t.created_on DESC
              LIMIT $limit",
            [':t' => defined('DEFAULT_TENANT_ID') ? DEFAULT_TENANT_ID : 1]
        ) ?: [];
    }

    public function downloadsFor(int $tokenId): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT * FROM app_install_downloads
              WHERE token_id = :tid ORDER BY downloaded_on DESC",
            [':tid' => $tokenId]
        ) ?: [];
    }

    /** Canonical APK path on disk. Missing file → null. */
    public function apkPath(): ?string
    {
        $p = self::APK_DIR . self::APK_LATEST;
        return is_file($p) ? $p : null;
    }

    /** Version + build info pulled from the mobile app.json if we can find it. */
    public function apkMeta(): array
    {
        $meta = ['version' => '—', 'built_on' => null, 'size' => null];
        $p = $this->apkPath();
        if ($p) {
            $meta['size']     = filesize($p);
            $meta['built_on'] = date('Y-m-d H:i', filemtime($p));
        }
        // Version from the mobile app.json if it's on the same machine
        $appJson = 'C:/MobileDev/RN/PacificDriverApp/app.json';
        if (is_file($appJson)) {
            $j = json_decode((string)file_get_contents($appJson), true);
            if (isset($j['expo']['version'])) $meta['version'] = (string)$j['expo']['version'];
        }
        return $meta;
    }
}
