<?php

declare(strict_types=1);

use App\Framework\Config\EnvKey;
use App\Framework\Config\Environment;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Cookies\Cookie;
use App\Framework\Http\Cookies\CookieService;
use App\Framework\Http\Cookies\SameSite;

describe('CookieService', function () {
    beforeEach(function () {
        $this->environment = new Environment([
            EnvKey::APP_ENV->value => 'development'
        ]);
        $this->service = new CookieService($this->environment);
    });

    describe('create()', function () {
        it('creates a standard cookie', function () {
            $cookie = $this->service->create(
                name: 'session',
                value: 'abc123'
            );

            expect($cookie->name)->toBe('session');
            expect($cookie->value)->toBe('abc123');
            expect($cookie->partitioned)->toBeFalse();
            expect($cookie->sameSite)->toBe(SameSite::Lax);
            expect($cookie->httpOnly)->toBeTrue();
        });

        it('sets secure flag in production', function () {
            $prodEnv = new Environment([
                EnvKey::APP_ENV->value => 'production'
            ]);
            $service = new CookieService($prodEnv);

            $cookie = $service->create('test', 'value');

            expect($cookie->secure)->toBeTrue();
        });

        it('does not set secure flag in development by default', function () {
            $cookie = $this->service->create('test', 'value');

            expect($cookie->secure)->toBeFalse();
        });

        it('respects explicit secure parameter', function () {
            $cookie = $this->service->create(
                name: 'test',
                value: 'value',
                secure: true
            );

            expect($cookie->secure)->toBeTrue();
        });

        it('sets expiration with maxAge', function () {
            $cookie = $this->service->create(
                name: 'test',
                value: 'value',
                maxAge: Duration::fromHours(1)
            );

            expect($cookie->expires)->toBeInt();
            expect($cookie->expires)->toBeGreaterThan(time());
        });
    });

    describe('createPartitioned()', function () {
        it('creates CHIPS partitioned cookie', function () {
            $cookie = $this->service->createPartitioned(
                name: 'widget',
                value: 'state123'
            );

            expect($cookie->partitioned)->toBeTrue();
            expect($cookie->secure)->toBeTrue();
            expect($cookie->sameSite)->toBe(SameSite::None);
            expect($cookie->httpOnly)->toBeTrue();
        });

        it('creates partitioned cookie with custom duration', function () {
            $cookie = $this->service->createPartitioned(
                name: 'analytics',
                value: 'user123',
                maxAge: Duration::fromDays(365)
            );

            expect($cookie->expires)->toBeInt();
            expect($cookie->partitioned)->toBeTrue();
        });

        it('allows httpOnly=false for JavaScript access', function () {
            $cookie = $this->service->createPartitioned(
                name: 'js_widget',
                value: 'data',
                httpOnly: false
            );

            expect($cookie->httpOnly)->toBeFalse();
            expect($cookie->partitioned)->toBeTrue();
        });
    });

    describe('createSessionCookie()', function () {
        it('creates session cookie without expiration', function () {
            $cookie = $this->service->createSessionCookie(
                name: 'session_id',
                value: 'xyz789'
            );

            expect($cookie->expires)->toBeNull();
            expect($cookie->httpOnly)->toBeTrue();
            expect($cookie->sameSite)->toBe(SameSite::Lax);
        });

        it('allows custom SameSite', function () {
            $cookie = $this->service->createSessionCookie(
                name: 'session',
                value: 'value',
                sameSite: SameSite::Strict
            );

            expect($cookie->sameSite)->toBe(SameSite::Strict);
        });
    });

    describe('createPartitionedSession()', function () {
        it('creates partitioned session cookie', function () {
            $cookie = $this->service->createPartitionedSession(
                name: 'widget_session',
                value: 'session123'
            );

            expect($cookie->expires)->toBeNull();
            expect($cookie->partitioned)->toBeTrue();
            expect($cookie->secure)->toBeTrue();
            expect($cookie->sameSite)->toBe(SameSite::None);
        });
    });

    describe('createRememberMeCookie()', function () {
        it('creates remember-me cookie with 30 day default', function () {
            $cookie = $this->service->createRememberMeCookie('token123');

            expect($cookie->name)->toBe('remember_me');
            expect($cookie->value)->toBe('token123');
            expect($cookie->httpOnly)->toBeTrue();
            expect($cookie->sameSite)->toBe(SameSite::Strict);
            expect($cookie->expires)->toBeInt();
        });

        it('allows custom duration', function () {
            $cookie = $this->service->createRememberMeCookie(
                value: 'token',
                maxAge: Duration::fromDays(7)
            );

            expect($cookie->expires)->toBeInt();
        });
    });

    describe('createAnalyticsCookie()', function () {
        it('creates partitioned analytics cookie', function () {
            $cookie = $this->service->createAnalyticsCookie('user123');

            expect($cookie->name)->toBe('_analytics');
            expect($cookie->value)->toBe('user123');
            expect($cookie->partitioned)->toBeTrue();
            expect($cookie->httpOnly)->toBeFalse(); // JS access needed
        });
    });

    describe('createWidgetStateCookie()', function () {
        it('creates partitioned widget state cookie', function () {
            $cookie = $this->service->createWidgetStateCookie(
                widgetId: 'chat',
                state: '{"minimized":false}'
            );

            expect($cookie->name)->toBe('widget_chat');
            expect($cookie->value)->toBe('{"minimized":false}');
            expect($cookie->partitioned)->toBeTrue();
            expect($cookie->httpOnly)->toBeTrue();
        });
    });

    describe('delete()', function () {
        it('creates deletion cookie with past expiration', function () {
            $cookie = $this->service->delete('session');

            expect($cookie->name)->toBe('session');
            expect($cookie->value)->toBe('');
            expect($cookie->expires)->toBeLessThan(time());
        });

        it('respects custom path', function () {
            $cookie = $this->service->delete('session', '/admin');

            expect($cookie->path)->toBe('/admin');
        });
    });

    describe('validateSize()', function () {
        it('validates cookie within size limit', function () {
            $cookie = new Cookie(
                name: 'test',
                value: 'small_value'
            );

            expect($this->service->validateSize($cookie))->toBeTrue();
        });

        it('rejects oversized cookie', function () {
            $cookie = new Cookie(
                name: 'huge',
                value: str_repeat('x', 5000)  // Exceeds 4KB limit
            );

            expect($this->service->validateSize($cookie))->toBeFalse();
        });
    });

    describe('isValidName()', function () {
        it('validates correct cookie names', function () {
            expect($this->service->isValidName('session_id'))->toBeTrue();
            expect($this->service->isValidName('user-token'))->toBeTrue();
            expect($this->service->isValidName('TOKEN123'))->toBeTrue();
        });

        it('rejects invalid cookie names', function () {
            expect($this->service->isValidName('session id'))->toBeFalse(); // space
            expect($this->service->isValidName('user@token'))->toBeFalse(); // @
            expect($this->service->isValidName('token=123'))->toBeFalse();  // =
            expect($this->service->isValidName('token;'))->toBeFalse();     // ;
        });
    });
});
