<?php

/**
 * Questo file è parte di Pongho.
 *
 * @author    Daniele Termini
 * @copyright Web Agency Meta Line S.r.l.
 * @package   Application\Admin
 */

namespace Application\Admin\Tests\Routes;

use Application\Admin\AdminSidebar\Sidebar;
use Application\Admin\Routes\PonghoRoute;
use Application\Admin\Utilities\HttpNotFoundResponseFactory;
use Application\Admin\Utilities\PonghoUrl;
use Application\Core\Model\Site;
use Application\Core\User;
use PHPUnit\Framework\MockObject\MockObject;
use Pongho\Http\Request;

class PonghoRouteTest extends \PHPUnit\Framework\TestCase
{
    public function testMatchWithUserNotLogged(): void
    {
        $sidebar = $this->createMock(\Application\Admin\AdminSidebar\Sidebar::class);

        $user = $this->createMock(User::class);

        $site = $this->createMock(Site::class);

        $site->expects($this->any())->method('getDomain')->willReturn('http://www.example.com');

        $pongho_url = $this->createMock(PonghoUrl::class);

        $pongho_url->expects($this->any())->method('currentUrl')->with('&')->willReturn('/pongho/?foo=bar');

        $request = Request::create('/pongho/');

        /**
         * @var \Application\Admin\AdminSidebar\Sidebar $sidebar
         * @var User                  $user
         * @var Site            $site
         * @var PonghoUrl  $pongho_url
         */

        $route = new PonghoRoute($sidebar, $user, $site, $pongho_url, $this->getResponseFactoryMock());
        $response = $route->match($request);

        $this->assertInstanceOf(\Pongho\Http\RedirectResponse::class, $response);

        /** @var \Pongho\Http\RedirectResponse $response */
        $this->assertEquals(
            'http://www.example.com/user/login/?redirect=%2Fpongho%2F%3Ffoo%3Dbar',
            $response->getRedirectUri()
        );
    }

    /**
     * @return array
     */
    public function routeProvider()
    {
        return [
            ['/pongho/', true, false],
            ['/pongho/path/', true, false],
            ['/pongho/path/a/', true, false],
            ['/pongho/path/c/', true, false],
            ['/pongho/pippo/', true, true],
            ['/example/', false, false],
        ];
    }

    /**
     * @dataProvider routeProvider
     * @param $uri
     * @param $is_pongho
     * @param $throw_exception
     */
    public function testMatch($uri, $is_pongho, $throw_exception): void
    {
        // Controlli per la correttezza della configurazione del test
        if ((!$is_pongho && $throw_exception) || ($is_pongho && !str_starts_with((string) $uri, '/pongho'))) {
            $this->fail('The PonghoRoute could not handle non-Pongho urls, check your test case');
        }

        $sidebar = $this->getSidebar($uri);
        $sidebar->addPanel('Panel A', 'panela', '', 0);
        $sidebar->addItem('item', '/', 'panela', 10, 'Controller');

        $sidebar->addPanel('Panel B', 'panelb', '', 10);
        $sidebar->addItem('item 0', '/path/', 'panelb', 10, 'Controller');
        $sidebar->addItem('item 1', '/path/a/', 'panelb', 20, 'Controller');
        $sidebar->addItem('item 2', '/path/b/', 'panelb', 30, 'Controller');
        $sidebar->addItem('item 3', '/path-other/', 'panelb', 40, 'Controller');

        $sidebar->addRoute('/path/c/', 'Controller');

        $route = new PonghoRoute(
            $sidebar,
            $this->getUserMock(true),
            $this->getSiteMock(),
            $this->getPonghoUrlMock(null),
            $this->getResponseFactoryMock()
        );

        $match = $route->match($this->getRequestMock($uri));

        if ($throw_exception) {
            // Mi assicuro che se non trova la rotta venga restituita un'istanza di Response
            $this->assertInstanceOf(\Pongho\Http\Response::class, $match);
        } else {
            $this->assertEquals($is_pongho, $match);
        }
    }

    /**
     * @param      $uri
     * @return \Application\Admin\AdminSidebar\Sidebar
     */
    protected function getSidebar($uri, $domain = 'https://example.com')
    {
        return new Sidebar(
            $this->getSiteMock($domain),
            $this->getRequestMock($uri),
            $this->getPonghoUrlMock($domain)
        );
    }

    protected function getSiteMock($domain = 'https://example.com'): Site|MockObject
    {
        $site = $this->createMock(Site::class);

        $site->expects($this->any())
            ->method('getDomain')
            ->will($this->returnValue($domain));

        return $site;
    }

    /**
     * @param $uri
     * @return MockObject|Request
     */
    protected function getRequestMock($uri)
    {
        return Request::create($uri);
    }

    /**
     * @param bool $logged
     */
    protected function getUserMock($logged = true): User|MockObject
    {
        $user = $this->createMock(User::class);

        $user->expects($this->any())->method('isLogged')->willReturn($logged);

        return $user;
    }

    /**
     * @param $domain
     */
    protected function getPonghoUrlMock($domain): PonghoUrl|MockObject
    {
        $pongho_url = $this->createMock(PonghoUrl::class);

        if ($domain) {
            $pongho_url
                ->expects($this->any())
                ->method('url')
                ->will($this->returnCallback(function ($path) use ($domain) {
                    return $domain . $path . '?foo=bar';
                }));
        } else {
            $pongho_url
                ->expects($this->any())
                ->method('url')
                ->will($this->returnCallback(function ($path) {
                    return $path . '?foo=bar';
                }));
        }

        return $pongho_url;
    }

    protected function getResponseFactoryMock(): HttpNotFoundResponseFactory|MockObject
    {
        $factory = $this->createMock(HttpNotFoundResponseFactory::class);

        $response = $this->createMock(\Pongho\Http\Response::class);

        $factory
            ->expects($this->any())
            ->method('getResponse')
            ->willReturn($response);

        return $factory;
    }
}
