<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ — NUCLEAR+++
// File: /home/ploi/core.automarketer-ai.com/public/src/Controllers/Plugin/PluginUpdateController.php
// Description: Plugin update check and authenticated ZIP download endpoints - NUCLEAR+++
// Last Edited: 2026-04-18
// ==================================================
namespace AMAI\Core\Controllers\Plugin;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use PDO;

class PluginUpdateController
{
    private const RELEASES_DIR  = '/home/ploi/releases';
    private const MIN_PHP       = '8.2';
    private const MIN_WP        = '6.0';
    private const CHANGELOG_URL = 'https://automarketer-ai.com/changelog';
    private const DOWNLOAD_BASE = 'https://core.automarketer-ai.com/api/plugin/download';

    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly PDO $pdo,
    ) {}

    // ── GET /api/plugin/update-check ─────────────────────────────────
    public function check(Request $request, Response $response): Response
    {
        $version = $this->resolveLatestVersion();

        if ($version === null) {
            $this->logger->error('plugin_update_check_no_release', [
                'releases_dir' => self::RELEASES_DIR,
            ]);
            $response->getBody()->write(json_encode([
                'error' => 'No release found',
            ], JSON_THROW_ON_ERROR));
            return $response
                ->withHeader('Content-Type', 'application/json')
                ->withStatus(503);
        }

        $payload = [
            'version'       => $version,
            'min_php'       => self::MIN_PHP,
            'min_wp'        => self::MIN_WP,
            'download_url'  => self::DOWNLOAD_BASE,
            'changelog_url' => self::CHANGELOG_URL,
        ];

        $response->getBody()->write(json_encode($payload, JSON_THROW_ON_ERROR));
        return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
    }

    // ── GET /api/plugin/download?license_key=xxx ─────────────────────
    public function download(Request $request, Response $response): Response
    {
        $params     = $request->getQueryParams();
        $licenseKey = isset($params['license_key']) ? trim((string) $params['license_key']) : '';

        if ($licenseKey === '') {
            $response->getBody()->write(json_encode(['error' => 'Missing license_key'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
        }

        if (!$this->validateLicenseKey($licenseKey)) {
            $this->logger->warning('plugin_download_invalid_key', [
                'key_prefix' => substr($licenseKey, 0, 8),
            ]);
            $response->getBody()->write(json_encode(['error' => 'Invalid or inactive license'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
        }

        $version = $this->resolveLatestVersion();

        if ($version === null) {
            $response->getBody()->write(json_encode(['error' => 'No release found'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(503);
        }

        $zipPath = self::RELEASES_DIR . '/automarketer-ai-v' . $version . '.zip';

        if (!is_file($zipPath)) {
            $this->logger->error('plugin_download_zip_missing', ['path' => $zipPath]);
            $response->getBody()->write(json_encode(['error' => 'Release file not found'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(503);
        }

        $size = filesize($zipPath);
        if ($size === false) {
            $this->logger->error('plugin_download_filesize_failed', ['path' => $zipPath]);
            $response->getBody()->write(json_encode(['error' => 'Release file unreadable'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(503);
        }

        $handle = fopen($zipPath, 'rb');
        if ($handle === false) {
            $this->logger->error('plugin_download_fopen_failed', ['path' => $zipPath]);
            $response->getBody()->write(json_encode(['error' => 'Release file unreadable'], JSON_THROW_ON_ERROR));
            return $response->withHeader('Content-Type', 'application/json')->withStatus(503);
        }

        $filename = 'automarketer-ai-v' . $version . '.zip';

        $response = $response
            ->withHeader('Content-Type', 'application/zip')
            ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
            ->withHeader('Content-Length', (string) $size)
            ->withHeader('Cache-Control', 'no-store');

        $stream = $response->getBody();
        while (!feof($handle)) {
            $chunk = fread($handle, 8192);
            if ($chunk === false) {
                break;
            }
            $stream->write($chunk);
        }
        fclose($handle);

        $this->logger->info('plugin_download_served', [
            'version'    => $version,
            'key_prefix' => substr($licenseKey, 0, 8),
        ]);

        return $response;
    }

    // ── Helpers ──────────────────────────────────────────────────────

    // Parse ZIP filenames in releases dir, return highest semver or null.
    private function resolveLatestVersion(): ?string
    {
        if (!is_dir(self::RELEASES_DIR)) {
            return null;
        }

        $files = scandir(self::RELEASES_DIR);
        if ($files === false) {
            return null;
        }

        $versions = [];
        foreach ($files as $file) {
            if (preg_match('/^automarketer-ai-v(\d+\.\d+\.\d+)\.zip$/', $file, $m)) {
                $versions[] = $m[1];
            }
        }

        if ($versions === []) {
            return null;
        }

        usort($versions, 'version_compare');
        return end($versions);
    }

    // Validate license key exists in tenants table with active status.
    private function validateLicenseKey(string $key): bool
    {
        $stmt = $this->pdo->prepare(
            'SELECT COUNT(*) FROM tenants WHERE license_key = :key AND status = :status LIMIT 1'
        );
        $stmt->execute([':key' => $key, ':status' => 'active']);
        return (int) $stmt->fetchColumn() > 0;
    }
}