<?php
namespace tests\Zeedhi\ZhuLog\Logger;

use Zeedhi\ZhuLog\Logger\Processor\DefaultProcessor;
use Zeedhi\ZhuLog\Logger\Exception;
use Zeedhi\ZhuLog\Logger\Logger;
use Zeedhi\ZhuLog\Logger\LoggerInfoProvider;
use Zeedhi\ZhuLog\Logger\Persistence\PersistenceInterface;
use Zeedhi\Framework\DTO\Request\Filter;
use Zeedhi\Framework\DTO\Request\DataSet;
use Zeedhi\Framework\DataSource;
use Zeedhi\Framework\HTTP\Request;
use Zeedhi\Framework\Log\Memory;

class LoggerTest extends \PHPUnit_Framework_TestCase {

    const DATE_REGEX = '[0-9]{4}\-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}';

    /** @var Logger */
    protected $logger;
    /** @var DefaultProcessor */
    protected $processor;

    protected function setUp() {
        $this->persistenceStrategy = $this->getMockBuilder(PersistenceInterface::class)
            ->setMethods(['logRequest', 'logResponse', 'logException'])
            ->disableOriginalConstructor()
            ->getMock();
        
        $this->processor = $this->getMockBuilder(DefaultProcessor::class)
            ->setMethods(['processRequest', 'processResponse', 'processException'])
            ->disableOriginalConstructor()
            ->getMock();

        $this->loggerInfoProvider  = $this->getMockBuilder(LoggerInfoProvider::class)
            ->setMethods(
                [
                    'getUserId',
                    'getOrganizationId',
                    'getProductId',
                    'getContainerName',
                    'getWidgetName',
                ]
            )
            ->disableOriginalConstructor()
            ->getMock();

        $_SERVER['PATH_INFO'] = '/test';
        $_SERVER['REQUEST_METHOD'] = 'POST';

        $this->logger = new Logger($this->persistenceStrategy, $this->loggerInfoProvider, $this->processor);
    }

    public function testLogRequestAndResponse() {
        $this->loggerInfoProvider->expects($this->once())
            ->method('getUserId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getOrganizationId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getProductId')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getContainerName')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getWidgetName')
            ->willReturn(null);

        $this->persistenceStrategy->expects($this->once())
            ->method('logRequest')
            ->with(
                'POST',
                '/uri',
                'DataSet',
                '[]',
                1,
                1,
                null,
                null,
                null
            );

        $this->persistenceStrategy->expects($this->once())
            ->method('logResponse')
            ->with(200, '[]');

        $fp = fopen('php://input', 'wb');
        fwrite($fp, '[]', 2);
        fclose($fp);
    
        $this->logger->logRequest('/uri', 'POST', 'DataSet');
        $this->logger->logResponse(200, []);
    }

    public function testDataSetRequest() {
        $this->loggerInfoProvider->expects($this->once())
            ->method('getUserId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getOrganizationId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getProductId')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getContainerName')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getWidgetName')
            ->willReturn(null);

        $this->persistenceStrategy->expects($this->once())
            ->method('logRequest')
            ->with(
                'POST',
                '/foo',
                'DataSet',
                '[]',
                1,
                1,
                null,
                null,
                null
            );

        $this->persistenceStrategy->expects($this->once())
            ->method('logResponse')
            ->with(200, '[]');

        $fp = fopen('php://input', 'wb');
        fwrite($fp, '[]', 2);
        fclose($fp);
    
        $this->logger->logRequest('/foo', 'POST', 'DataSet');
        $this->logger->logResponse(200,
            ['dataset' =>
                [
                    'foo' => [1, 2, 3, 4],
                ],
            ]
        );
    }

    public function testPersistenceError() {
        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Database error');

        $this->loggerInfoProvider->expects($this->once())
            ->method('getUserId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getOrganizationId')
            ->willReturn(1);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getProductId')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getContainerName')
            ->willReturn(null);

        $this->loggerInfoProvider->expects($this->once())
            ->method('getWidgetName')
            ->willReturn(null);
        
        $this->persistenceStrategy->expects($this->once())
            ->method('logRequest')
            ->with(
                'POST',
                '/foo',
                'DataSet',
                '[]',
                1,
                1,
                null,
                null,
                null
            )
            ->will($this->throwException(new Exception('Database error')));

        $this->persistenceStrategy->expects($this->never())
            ->method('logResponse');

        $fp = fopen('php://input', 'wb');
        fwrite($fp, '[]', 2);
        fclose($fp);
        
        $this->logger->logRequest('/foo', 'POST', 'DataSet');
        $this->logger->logResponse(500, ['error' => 'Invalid request']);
    }
}