<?php

declare(strict_types=1);

namespace App\Framework\Http\Cookies;

use App\Framework\Config\EnvKey;
use App\Framework\Config\Environment;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Service for creating cookies with framework-compliant patterns.
 *
 * Supports CHIPS (Cookies Having Independent Partitioned State) for third-party contexts.
 */
final readonly class CookieService
{
    private const MAX_COOKIE_SIZE = 4096;

    public function __construct(
        private Environment $environment
    ) {
    }

    /**
     * Create a standard cookie.
     */
    public function create(
        string $name,
        string $value,
        ?Duration $maxAge = null,
        string $path = '/',
        ?string $domain = null,
        ?bool $secure = null,
        bool $httpOnly = true,
        SameSite $sameSite = SameSite::Lax
    ): Cookie {
        // Security: enforce secure in production
        $isProduction = $this->environment->get(EnvKey::APP_ENV) === 'production';
        $secure = $secure ?? $isProduction;

        // Calculate expiration timestamp
        $expires = $maxAge ? Timestamp::now()->add($maxAge)->toTimestamp() : null;

        return new Cookie(
            name: $name,
            value: $value,
            expires: $expires,
            path: $path,
            domain: $domain ?? '',
            secure: $secure,
            httpOnly: $httpOnly,
            sameSite: $sameSite,
            partitioned: false
        );
    }

    /**
     * Create a CHIPS (partitioned) cookie for third-party contexts.
     *
     * Use cases:
     * - Embedded widgets (chat, support, social media)
     * - Third-party analytics in iframes
     * - Payment provider embeds (Stripe, PayPal)
     * - OAuth flows in iframes
     *
     * Requirements:
     * - Must be Secure (HTTPS only)
     * - Must have SameSite=None
     * - Partitioned attribute set
     *
     * @throws \InvalidArgumentException if cookie validation fails
     */
    public function createPartitioned(
        string $name,
        string $value,
        ?Duration $maxAge = null,
        string $path = '/',
        ?string $domain = null,
        bool $httpOnly = true
    ): Cookie {
        $expires = $maxAge ? Timestamp::now()->add($maxAge)->toTimestamp() : null;

        // CHIPS cookies MUST be Secure and SameSite=None
        return new Cookie(
            name: $name,
            value: $value,
            expires: $expires,
            path: $path,
            domain: $domain ?? '',
            secure: true,              // Required for Partitioned
            httpOnly: $httpOnly,
            sameSite: SameSite::None,  // Required for Partitioned
            partitioned: true          // CHIPS attribute
        );
    }

    /**
     * Create a session cookie (no expiration).
     */
    public function createSessionCookie(
        string $name,
        string $value,
        bool $httpOnly = true,
        SameSite $sameSite = SameSite::Lax
    ): Cookie {
        return $this->create(
            name: $name,
            value: $value,
            maxAge: null,  // Session cookie
            httpOnly: $httpOnly,
            sameSite: $sameSite
        );
    }

    /**
     * Create a partitioned session cookie for third-party embeds.
     */
    public function createPartitionedSession(
        string $name,
        string $value,
        bool $httpOnly = true
    ): Cookie {
        return new Cookie(
            name: $name,
            value: $value,
            expires: null,  // Session cookie
            path: '/',
            domain: '',
            secure: true,
            httpOnly: $httpOnly,
            sameSite: SameSite::None,
            partitioned: true
        );
    }

    /**
     * Create a remember-me cookie (typically 30 days).
     */
    public function createRememberMeCookie(
        string $value,
        ?Duration $maxAge = null
    ): Cookie {
        return $this->create(
            name: 'remember_me',
            value: $value,
            maxAge: $maxAge ?? Duration::fromDays(30),
            httpOnly: true,
            sameSite: SameSite::Strict
        );
    }

    /**
     * Create a third-party analytics cookie (partitioned).
     */
    public function createAnalyticsCookie(
        string $userId,
        ?Duration $maxAge = null
    ): Cookie {
        return $this->createPartitioned(
            name: '_analytics',
            value: $userId,
            maxAge: $maxAge ?? Duration::fromDays(365),
            httpOnly: false  // JavaScript access needed for analytics
        );
    }

    /**
     * Create a third-party widget state cookie (partitioned).
     */
    public function createWidgetStateCookie(
        string $widgetId,
        string $state,
        ?Duration $maxAge = null
    ): Cookie {
        return $this->createPartitioned(
            name: "widget_{$widgetId}",
            value: $state,
            maxAge: $maxAge ?? Duration::fromDays(30),
            httpOnly: true
        );
    }

    /**
     * Create a cookie deletion instruction.
     *
     * Sets expiration to past time to delete cookie.
     */
    public function delete(string $name, string $path = '/'): Cookie
    {
        // Set expiration to 1 second ago (past time deletes cookie)
        return new Cookie(
            name: $name,
            value: '',
            expires: time() - 1,
            path: $path,
            domain: '',
            secure: false,
            httpOnly: false,
            sameSite: SameSite::Lax,
            partitioned: false
        );
    }

    /**
     * Validate cookie size is within browser limits.
     */
    public function validateSize(Cookie $cookie): bool
    {
        return strlen($cookie->toHeaderString()) <= self::MAX_COOKIE_SIZE;
    }

    /**
     * Check if cookie name is valid.
     */
    public function isValidName(string $name): bool
    {
        // Cookie name must not contain: ( ) < > @ , ; : \ " / [ ] ? = { } space tab
        return preg_match('/^[a-zA-Z0-9_-]+$/', $name) === 1;
    }
}
