<?php

namespace SilverStripe\Control\Tests;

use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Control\CookieJar;
use SilverStripe\Control\Cookie;

class CookieTest extends SapphireTest
{

    protected function setUp()
    {
        parent::setUp();
        Injector::inst()->registerService(new CookieJar($_COOKIE), 'SilverStripe\\Control\\Cookie_Backend');
    }

    /**
     * Check a new cookie inst will be loaded with the superglobal by default
     */
    public function testCheckNewInstTakesSuperglobal()
    {
        //store the superglobal state
        $existingCookies = $_COOKIE;

        //set a mock state for the superglobal
        $_COOKIE = array(
            'cookie1' => 1,
            'cookie2' => 'cookies',
            'cookie3' => 'test',
            'cookie_4' => 'value',
        );

        Injector::inst()->unregisterNamedObject('SilverStripe\\Control\\Cookie_Backend');

        $this->assertEquals($_COOKIE['cookie1'], Cookie::get('cookie1'));
        $this->assertEquals($_COOKIE['cookie2'], Cookie::get('cookie2'));
        $this->assertEquals($_COOKIE['cookie3'], Cookie::get('cookie3'));
        $this->assertEquals($_COOKIE['cookie_4'], Cookie::get('cookie.4'));
        $this->assertEquals($_COOKIE['cookie_4'], Cookie::get('cookie_4'));

        //for good measure check the CookieJar hasn't stored anything extra
        $this->assertEquals($_COOKIE, Cookie::get_inst()->getAll(false));

        //restore the superglobal state
        $_COOKIE = $existingCookies;
    }

    /**
     * Check we don't mess with super globals when manipulating cookies
     *
     * State should be managed sperately to the super global
     */
    public function testCheckSuperglobalsArentTouched()
    {

        //store the current state
        $before = $_COOKIE;

        //change some cookies
        Cookie::set('cookie', 'not me');
        Cookie::force_expiry('cookie2');

        //assert it hasn't changed
        $this->assertEquals($before, $_COOKIE);
    }

    /**
     * Check we can actually change a backend
     */
    public function testChangeBackend()
    {

        Cookie::set('test', 'testvalue');

        $this->assertEquals('testvalue', Cookie::get('test'));

        Injector::inst()->registerService(new CookieJar(array()), 'SilverStripe\\Control\\Cookie_Backend');

        $this->assertEmpty(Cookie::get('test'));
    }

    /**
     * Check we can actually get the backend inst out
     */
    public function testGetInst()
    {

        $inst = new CookieJar(array('test' => 'testvalue'));

        Injector::inst()->registerService($inst, 'SilverStripe\\Control\\Cookie_Backend');

        $this->assertEquals($inst, Cookie::get_inst());

        $this->assertEquals('testvalue', Cookie::get('test'));
    }

    /**
     * Test that we can set and get cookies
     */
    public function testSetAndGet()
    {
        $this->assertEmpty(Cookie::get('testCookie'));

        //set a test cookie
        Cookie::set('testCookie', 'testVal');

        //make sure it was set
        $this->assertEquals('testVal', Cookie::get('testCookie'));

        //make sure we can distinguise it from ones that were "existing"
        $this->assertEmpty(Cookie::get('testCookie', false));
    }

    /**
     * Test that we can distinguish between vars that were loaded on instantiation
     * and those added later
     */
    public function testExistingVersusNew()
    {
        //load with a cookie
        $cookieJar = new CookieJar(
            array(
                'cookieExisting' => 'i woz here',
            )
        );
        Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend');

        //set a new cookie
        Cookie::set('cookieNew', 'i am new');

        //check we can fetch new and old cookie values
        $this->assertEquals('i woz here', Cookie::get('cookieExisting'));
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));
        $this->assertEquals('i am new', Cookie::get('cookieNew'));
        //there should be no original value for the new cookie
        $this->assertEmpty(Cookie::get('cookieNew', false));

        //change the existing cookie, can we fetch the new and old value
        Cookie::set('cookieExisting', 'i woz changed');

        $this->assertEquals('i woz changed', Cookie::get('cookieExisting'));
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));

        //check we can get all cookies
        $this->assertEquals(
            array(
                'cookieExisting' => 'i woz changed',
                'cookieNew' => 'i am new',
            ),
            Cookie::get_all()
        );

        //check we can get all original cookies
        $this->assertEquals(
            array(
                'cookieExisting' => 'i woz here',
            ),
            Cookie::get_all(false)
        );
    }

    /**
     * Check we can remove cookies and we can access their original values
     */
    public function testForceExpiry()
    {
        //load an existing cookie
        $cookieJar = new CookieJar(
            array(
                'cookieExisting' => 'i woz here',
            )
        );
        Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend');

        //make sure it's available
        $this->assertEquals('i woz here', Cookie::get('cookieExisting'));

        //remove the cookie
        Cookie::force_expiry('cookieExisting');

        //check it's gone
        $this->assertEmpty(Cookie::get('cookieExisting'));

        //check we can get it's original value
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));


        //check we can add a new cookie and remove it and it doesn't leave any phantom values
        Cookie::set('newCookie', 'i am new');

        //check it's set by not recieved
        $this->assertEquals('i am new', Cookie::get('newCookie'));
        $this->assertEmpty(Cookie::get('newCookie', false));

        //remove it
        Cookie::force_expiry('newCookie');

        //check it's neither set nor reveived
        $this->assertEmpty(Cookie::get('newCookie'));
        $this->assertEmpty(Cookie::get('newCookie', false));
    }
}