<?php
/**
 * Bunny CDN Adapter
 *
 * Implements CDN provider interface for Bunny.net integration including
 * URL purging, tag-based purging, and wildcard purge support.
 *
 * @package Mamba\Modules\CDN\Services
 * @since   1.0.0
 */

namespace Mamba\Modules\CDN\Services;

/**
 * Class BunnyAdapter
 *
 * Bunny.net CDN adapter that handles cache purging via the Bunny API,
 * supports CDN-Tag headers, and provides wildcard purge capabilities.
 *
 * @since 1.0.0
 */
final class BunnyAdapter implements Provider {
    private string $pullZoneId;
    private string $apiKey;
    private bool $sendCdnTags;
    private bool $useWildcardPurges;
    private const API_BASE = 'https://api.bunny.net';
    
    public function __construct(
        string $pullZoneId,
        string $apiKey,
        bool $sendCdnTags = true,
        bool $useWildcardPurges = false
    ) {
        $this->pullZoneId = $pullZoneId;
        $this->apiKey = $apiKey;
        $this->sendCdnTags = $sendCdnTags;
        $this->useWildcardPurges = $useWildcardPurges;
    }
    
    public function isConnected(): bool {
        return !empty($this->pullZoneId) && !empty($this->apiKey);
    }
    
    public function getName(): string {
        return 'bunny';
    }
    
    public function purgeUrls(array $urls, array $headerCombos = []): Result {
        if (empty($urls)) {
            return Result::success('No URLs to purge');
        }
        
        $chunks = array_chunk(array_values(array_unique($urls)), 50);
        $firstError = null;

        foreach ($chunks as $chunk) {
            $res = $this->post("/pullzone/{$this->pullZoneId}/purgeCache", ['urls' => $chunk]);
            if (!$res->isSuccess()) {
                $firstError ??= $res;
            }
        }
        return $firstError ?: Result::success('Purge successful');
    }
    
    public function purgeTags(array $tags): Result {
        if (empty($tags)) {
            return Result::success('No tags to purge');
        }
        
        // Bunny uses CacheTag parameter for tag purging
        $payload = ['CacheTag' => implode(',', array_unique($tags))];
        return $this->post("/pullzone/{$this->pullZoneId}/purgeCache", $payload);
    }
    
    public function purgeAll(): Result {
        $payload = ['purgeAll' => true];
        return $this->post("/pullzone/{$this->pullZoneId}/purgeCache", $payload);
    }
    
    public function applyRecommendedSettings(): Result {
        // Bunny doesn't have equivalent settings to apply
        return Result::success('No recommended settings to apply for Bunny.net');
    }
    
    public function testConnection(): Result {
        $result = $this->get("/pullzone/{$this->pullZoneId}");
        if ($result->isSuccess()) {
            $data = $result->getData();
            $zoneName = $data['Name'] ?? 'Unknown';
            return Result::success("Connected to Bunny.net pull zone: {$zoneName}");
        }
        return $result;
    }
    
    /**
     * Purge media family using wildcard (if enabled)
     */
    public function purgeMediaFamily(int $attachmentId): Result {
        if (!$this->useWildcardPurges) {
            return Result::success('Wildcard purges disabled');
        }
        
        $uploadDir = wp_upload_dir();
        $attachment = get_post($attachmentId);
        
        if (!$attachment || $attachment->post_type !== 'attachment') {
            return Result::error('Invalid attachment ID');
        }
        
        $filePath = get_attached_file($attachmentId);
        if (!$filePath) {
            return Result::error('Could not get attachment file path');
        }
        
        // Get the base filename without extension
        $pathInfo = pathinfo($filePath);
        $baseName = $pathInfo['filename'];
        $dir = $pathInfo['dirname'];
        
        // Create wildcard pattern for the file family
        $wildcardPattern = $dir . '/' . $baseName . '*';
        
        // Convert to URL pattern
        $uploadUrl = $uploadDir['baseurl'];
        $uploadPath = $uploadDir['basedir'];
        $relativePath = str_replace($uploadPath, '', $wildcardPattern);
        $urlPattern = $uploadUrl . $relativePath;
        
        $payload = ['urls' => [$urlPattern]];
        return $this->post("/pullzone/{$this->pullZoneId}/purgeCache", $payload);
    }
    
    /**
     * Make a GET request to Bunny API
     */
    private function get(string $endpoint): Result {
        return $this->request('GET', $endpoint);
    }
    
    /**
     * Make a POST request to Bunny API
     */
    private function post(string $endpoint, array $data): Result {
        return $this->request('POST', $endpoint, $data);
    }
    
    /**
     * Make a request to Bunny API with retries and error handling
     */
    private function request(string $method, string $endpoint, array $data = []): Result {
        $url = self::API_BASE . $endpoint;
        
        $args = [
            'method' => $method,
            'headers' => [
                'AccessKey' => $this->apiKey,
                'Content-Type' => 'application/json',
                'User-Agent' => 'Mamba-WooCommerce-CDN/1.0'
            ],
            'timeout' => 30,
            'sslverify' => true
        ];
        
        if (!empty($data)) {
            $args['body'] = wp_json_encode($data);
        }
        
        $response = wp_remote_request($url, $args);
        
        if (is_wp_error($response)) {
            return Result::error('Request failed: ' . $response->get_error_message());
        }
        
        $statusCode = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if ($statusCode >= 200 && $statusCode < 300) {
            return Result::success('Request successful', $data ?: []);
        }
        
        $errorMessage = 'API request failed';
        if ($data && isset($data['Message'])) {
            $errorMessage = $data['Message'];
        }
        
        return Result::error($errorMessage, $statusCode, $data ?: []);
    }
}
