<?php

namespace SmartSyncS3\Models;
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
use Aws\Acm\AcmClient;
use Aws\CloudFront\CloudFrontClient;
use Aws\Result;
use Aws\S3\S3Client;
use Aws\Sts\StsClient;

/**
 * Class CloudConfig
 * Represents the configuration for AWS services, including S3, CloudFront, and ACM.
 * Provides methods to manage and interact with these services using AWS SDK clients.
 *
 *
 * Developer Note: In order to add the new config here you need to add a public access modifier in the
 * class, add it in  toArray and fromArray methods of this class.
 *
 * @package SmartSyncS3
 * @author Infinitie Technologies
 *
 *  Credits:
 *  - Developed by Infinitie Technologies
 *  - Maintained by the Infintitie Development Team
 *  - Inspired by best practices for WordPress plugin development
 */
class SMARL_CloudConfig
{

    /**
     * @var string $bucket The name of the S3 bucket.
     */
    public string $bucket;

    /**
     * @var string $serviceProvider Key for service provider (aws or digitalocean)
     */
    public string $serviceProvider = "aws";

    /**
     * @var string $region The AWS region for the S3 bucket and other services. Defaults to "us-east-1".
     */
    public string $region = "us-east-1";

    /**
     * @var string $cloudfrontUrl The URL of the CloudFront distribution associated with the S3 bucket.
     */
    public string $cloudfrontUrl;

    /**
     * @var string $url The primary URL for accessing the bucket or CloudFront distribution.
     */
    public string $url;

    /**
     * @var string $iamKey The IAM user's access key for authenticating AWS API calls.
     */
    public string $iamKey;

    /**
     * @var string $iamSecret The IAM user's secret key for authenticating AWS API calls.
     */
    public string $iamSecret;

    /**
     * @var string $distributionId The ID of the CloudFront distribution, if applicable.
     */
    public string $distributionId;

    /**
     * @var bool $useCloudFront Indicates whether CloudFront is being used with the S3 bucket.
     */
    public bool $useCloudFront;

    /**
     * @var string $urlType The type of URL being used (e.g., 'cloudfront' or 'custom').
     */
    public string $urlType;

    public bool $canEnableMediaCDN = false;


    public bool $canEnableAssetCDN = false;


    public bool $mediaAutoDelete = false;


    /**
     * @var bool $useCdnForMedia Weather to serve the media from the cdn url or from local url.(Primarily Images and videos)
     */
    public bool $useCdnForMedia = false;


    public string $cloudflareEndpoint = "";


    public string $googleServicePath = "";

    public string $cloudflareUrl = "";

    /**
     * @var ?SMARL_CustomUrl $customUrl Custom Url Config.
     */
    public ?SMARL_CustomUrl $customUrl;

    /**
     * @var bool $useCdnForAssets Weather to serve the assets from the cdn url or from local url.(Primarily js and css)
     */
    public bool $useCdnForAssets = false;

    /**
     * Creates a CloudConfig instance from an associative array. (factory method)
     *
     * @param array $data The configuration data as an array.
     * @return SMARL_CloudConfig The initialized CloudConfig instance.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public static function fromArray(array $data): SMARL_CloudConfig
    {
        $config = new SMARL_CloudConfig();

        $config->bucket = $data['bucket'] ?? '';
            $config->region= $data['region'] ?? '';
            $config->cloudfrontUrl= $data['cloudfront_url'] ?? '';
            $config->url= $data['url'] ?? '';
            $config->iamKey= $data['iam_key'] ?? '';
            $config->iamSecret= $data['iam_secret'] ?? '';
            $config->distributionId= $data['distribution_id'] ?? '';
            $config->useCloudFront= $data['use_cloudfront'] ?? false;
            $config->urlType= $data['url_type'] ?? 'custom';
            $config->useCdnForAssets= $data['use_cdn_for_assets'] ?? false;
            $config->serviceProvider= $data['service_provider'] ?? "";
            $config->useCdnForMedia= $data['use_cdn_for_media'] ?? false;
            $config->canEnableMediaCDN= $data['canEnableMediaCDN'] ?? false;
            $config->mediaAutoDelete= $data['mediaAutoDelete'] ?? false;
            $config->canEnableAssetCDN= $data['canEnableAssetCDN'] ?? false;
            $config->googleServicePath= $data['googleServicePath'] ?? false;
            $config->cloudflareEndpoint= $data['cloudflareEndpoint'] ?? false;
            $config->cloudflareUrl= $data['cloudflareUrl'] ?? false;

            return $config;

    }

    /**
     * Converts the CloudConfig instance into an associative array.
     *
     * @return array The configuration data as an array.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function toArray(): array
    {
        return [
            'bucket' => $this->bucket,
            'region' => $this->region,
            'cloudfront_url' => $this->cloudfrontUrl,
            'url' => $this->url,
            'iam_key' => $this->iamKey,
            'iam_secret' => $this->iamSecret,
            'distribution_id' => $this->distributionId,
            'use_cloudfront' => $this->useCloudFront,
            'url_type' => $this->urlType,
            "use_cdn_for_media" => $this->useCdnForMedia,
            "use_cdn_for_assets" => $this->useCdnForAssets,
            "service_provider" => $this->serviceProvider,
            "mediaAutoDelete" => $this->mediaAutoDelete,
            "canEnableMediaCDN" => $this->canEnableMediaCDN,
            "canEnableAssetCDN" => $this->canEnableAssetCDN,
            "googleServicePath" => $this->googleServicePath,
            "cloudflareEndpoint" => $this->cloudflareEndpoint,
            "cloudflareUrl" => $this->cloudflareUrl,
        ];
    }

    /**
     * Saves the current CloudConfig instance to the WordPress options table.
     * @return void
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function save(): void
    {
        update_option('smarl_settings', $this->toArray());
    }

    /**
     * Loads the CloudConfig instance from the WordPress options table.
     *
     * @return SMARL_CloudConfig|null The loaded CloudConfig instance, or null if no settings exist.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public static function load(): ?SMARL_CloudConfig
    {

        $data = get_option('smarl_settings', []);


        $config = self::fromArray($data);
        $config->customUrl = SMARL_CustomUrl::load();

        return $config;


    }

    /**
     * Deletes the stored AWS S3 and CloudFront settings from the WordPress options table.
     * @return void
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public static function delete(): void
    {
        delete_option('smarl_settings');
    }

    /**
     * Initializes the AWS configuration in the WordPress options table with default values.
     * @return void
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public static function init(): void
    {
        add_option('smarl_settings', []);
    }

    /**
     * Returns an S3Client instance configured for the specified or default region.
     *
     * @param string $region Optional region for the S3 client. Defaults to the instance's region.
     * @return S3Client The initialized S3 client.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function getS3Client(string $region = ""): S3Client |null
    {
        if ($region == "") {
            $region = $this->region;
        }


        if ($this->serviceProvider == "aws") {
            return new S3Client([
                'region' => $region,
                'version' => 'latest',
                'credentials' => [
                    'key' => $this->iamKey,
                    'secret' => $this->iamSecret,
                ],
            ]);

        }

        if ($this->serviceProvider == "cloudflare") {
            return new S3Client([
                'version' => 'latest',
                'region'  => 'auto', // Cloudflare R2 uses 'auto' for region
                'endpoint' => $this->cloudflareEndpoint,
                'credentials' => [
                    'key' => $this->iamKey,
                    'secret' => $this->iamSecret,
                ],
                'use_path_style_endpoint' => true,
            ]);
        }

        if ($this->serviceProvider == "digitalocean") {
                    return new S3Client([
            'region' => $region,
            'version' => 'latest',
            'endpoint' => "https://$region.digitaloceanspaces.com",
            'credentials' => [
                'key' => $this->iamKey,
                'secret' => $this->iamSecret,
            ],
        ]);


        }


        if ($this->serviceProvider == "wasabi") {

            return new S3Client([
                'region' => $region,
                'version' => 'latest',
                'endpoint' => ($region === "us-east-1") ? "https://s3.wasabisys.com" :"https://s3.$region.wasabisys.com",
                'credentials' => [
                    'key' => $this->iamKey,
                    'secret' => $this->iamSecret,
                ],
            ]);
        }
        return null;
    }

    /**
     * Returns a CloudFrontClient instance configured for the us-east-1 region.
     *
     * @return CloudFrontClient The initialized CloudFront client.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function getCloudfrontClient(): CloudFrontClient
    {

        return new CloudFrontClient([
            'region' => 'us-east-1',
            'version' => 'latest',
            'credentials' => [
                'key' => $this->iamKey,
                'secret' => $this->iamSecret,
            ],
        ]);
    }

    /**
     * Returns an StsClient instance configured for the specified or default region.
     *
     * @param string $region Optional region for the STS client. Defaults to the instance's region.
     * @return StsClient The initialized STS client.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function getStsClient(string $region = ""): StsClient
    {
        if ($region == "") {
            $region = $this->region;
        }
        return new StsClient([
            'region' => $region,
            'version' => 'latest',
            'credentials' => [
                'key' => $this->iamKey,
                'secret' => $this->iamSecret,
            ],
        ]);
    }

    /**
     * Returns an AcmClient instance configured for the us-east-1 region.
     *
     * @return AcmClient The initialized ACM client.
     * @developer Jay Parmar <infinitie.jay@gmail.com>
     */
    public function getAcmClient(): AcmClient
    {
        return new AcmClient([
            'region' => 'us-east-1',
            'version' => 'latest',
            'credentials' => [
                'key' => $this->iamKey,
                'secret' => $this->iamSecret,
            ],
        ]);
    }


    public function createDigitalOceanSpace($spaceName): array
    {
        $service = 's3';
        $host = "$this->region.digitaloceanspaces.com";
        $amzDate = gmdate('Ymd\THis\Z');
        $datestamp = gmdate('Ymd');

        // Step 1: Create canonical request
        $httpRequestMethod = 'PUT';
        $canonicalUri = '/';
        $canonicalQueryString = '';
        $payloadHash = hash('sha256', '');

        $canonicalHeaders = "host:$spaceName.$host\n"
            . "x-amz-content-sha256:$payloadHash\n"
            . "x-amz-date:$amzDate\n";

        $signedHeaders = 'host;x-amz-content-sha256;x-amz-date';

        $canonicalRequest = "$httpRequestMethod\n$canonicalUri\n$canonicalQueryString\n$canonicalHeaders\n$signedHeaders\n$payloadHash";

        // Step 2: Create string to sign
        $algorithm = 'AWS4-HMAC-SHA256';
        $credentialScope = "$datestamp/$this->region/$service/aws4_request";
        $stringToSign = "$algorithm\n$amzDate\n$credentialScope\n" . hash('sha256', $canonicalRequest);

        // Step 3: Calculate signature
        $kSecret = "AWS4$this->iamSecret";
        $kDate = hash_hmac('sha256', $datestamp, $kSecret, true);
        $kRegion = hash_hmac('sha256', $this->region, $kDate, true);
        $kService = hash_hmac('sha256', $service, $kRegion, true);
        $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
        $signature = hash_hmac('sha256', $stringToSign, $kSigning);

        // Step 4: Create authorization header
        $authorizationHeader = "$algorithm "
            . "Credential=$this->iamKey/$credentialScope, "
            . "SignedHeaders=$signedHeaders, "
            . "Signature=$signature";

        // Prepare headers
        $headers = [
            "Host" => "$spaceName.$host",
            "X-Amz-Content-Sha256" => $payloadHash,
            "X-Amz-Date" => $amzDate,
            "Authorization" => $authorizationHeader
        ];

        // Send the request using wp_remote_request
        $url = "https://$spaceName.$host";
        $response = wp_remote_request($url, [
            'method'    => 'PUT',
            'headers'   => $headers,
            'body'      => '',
            'timeout'   => 45, // Optional, adjust based on your needs
        ]);

        // Check the response
        if (is_wp_error($response)) {
            return [
                'success' => false,
                'response' => $response->get_error_message(),
                'code' => $response->get_error_code(),
                'error' => 'Request failed.',
            ];
        }

        // Get the response status and body
        $httpCode = wp_remote_retrieve_response_code($response);
        $responseBody = wp_remote_retrieve_body($response);

        return [
            'success' => ($httpCode === 200),
            'response' => $responseBody,
            'code' => $httpCode,
            'error' => null,
        ];
    }


    public function putS3Object($file_name, $file_path): Result
    {
        return $this->getS3Client()->putObject([
            'Bucket' => $this->bucket,
            'Key' => $file_name,
            'SourceFile' => $file_path,
            'ACL' => 'public-read',
        ]);
    }

}
