<?php


namespace App\Model\Facades\Writers;


use App\Model\Entities\Components\Event\Event;
use App\Model\Entities\Components\Forum\Forum;
use App\Model\Entities\Components\Gallery\Gallery;
use App\Model\Entities\Components\News\NewsContainer;
use App\Model\Entities\MmorpgGuild;
use App\Model\Entities\PortalSite;
use App\Model\Entities\Security\Role;
use App\Model\Entities\Templates\EventTemplate;
use App\Model\Entities\Templates\ForumTemplate;
use App\Model\Entities\Templates\GalleryTemplate;
use App\Model\Entities\Templates\NewsTemplate;
use App\Model\Entities\Templates\SiteTemplate;
use App\Model\Exceptions\Db\RecordAlreadyCreatedException;
use App\Model\Facades\Readers\FactionReader;
use App\Model\Facades\Readers\GameReader;
use App\Model\Facades\Readers\PermissionReader;
use App\Model\Facades\Readers\ServerReader;
use App\Model\Facades\Readers\SiteReader;
use App\Model\Facades\Readers\TemplateReader;
use App\Model\Interfaces\ICommonRecord;
use App\Model\Interfaces\IGameData;
use App\Model\Interfaces\ISite;
use App\Model\Services\GameService;
use App\Model\Services\SiteService;
use Throwable;

class SiteWriter extends BaseWriter
{
    /** @var  SiteService */
    protected $siteService;
    /** @var  GameReader */
    protected $gameReader;
    /** @var  ServerReader */
    protected $serverReader;
    /** @var  FactionReader */
    protected $factionReader;
    /** @var  TemplateReader */
    protected $templateReader;
    /** @var  TemplateWriter */
    protected $templateWriter;
    /** @var  PermissionWriter */
    protected $permissionWriter;
    /** @var  PermissionReader */
    protected $permissionReader;
    /** @var  GameService */
    protected $gameService;
    /** @var  SiteReader */
    protected $siteReader;

    /**
     * @param SiteService $siteService
     * @param GameReader $gameReader
     * @param ServerReader $serverReader
     * @param FactionReader $factionReader
     * @param TemplateReader $templateReader
     * @param TemplateWriter $templateWriter
     * @param PermissionReader $permissionReader
     * @param PermissionWriter $permissionWriter
     * @param GameService $gameService
     * @param SiteReader $siteReader
     */
    public function inject(SiteService $siteService, GameReader $gameReader, ServerReader $serverReader,
                           FactionReader $factionReader, TemplateReader $templateReader, TemplateWriter $templateWriter,
                           PermissionReader $permissionReader, PermissionWriter $permissionWriter, GameService $gameService,
                           SiteReader $siteReader)
    {
        $this->siteService = $siteService;
        $this->gameReader = $gameReader;
        $this->serverReader = $serverReader;
        $this->factionReader = $factionReader;
        $this->templateReader = $templateReader;
        $this->templateWriter = $templateWriter;
        $this->permissionReader = $permissionReader;
        $this->permissionWriter = $permissionWriter;
        $this->gameService = $gameService;
        $this->siteReader = $siteReader;
    }

    public function joinGuild($guild)
    {
        $guildEntity = $this->siteReader->validateGuild($guild);
        $defaultRole = $this->permissionReader->findSiteDefaultRole($guildEntity);

        try {
            $this->beginTransaction();
            $user = $this->getLoggedInUser();
            $guildEntity->addMember($user);
            $user->addRole($defaultRole);
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    /**
     * @param string $name
     * @param null|Role $guest
     * @return PortalSite
     * @throws RecordAlreadyCreatedException
     * @throws Throwable
     */
    public function createPortalSite(string $name, $guest = null) : PortalSite
    {
        $site = $this->siteService->findPortalSite();
        if ($site !== null) {
            throw new RecordAlreadyCreatedException();
        }

        try {
            $this->beginTransaction();
            $site = $this->siteService->createPortalSite($this->getLoggedInUser(), $name);

            if (!$guest instanceof Role) {
                $guest = $this->permissionReader->findGuestRole();
            }
            $member = $this->permissionWriter->createRole('Member', $site, $guest);
            $member->setAsDefaultRole();
            $moderator = $this->permissionWriter->createRole('Moderator', $site, $member);
            $admin = $this->permissionWriter->createRole('Admin', $site, $moderator);

            $siteTemplate = $this->addSiteTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $admin, $moderator);
            $site->setTemplate($siteTemplate);

            $this->addNewsTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $moderator);
            $this->addNewsTemplate($site, 'members only', 'Content is accessible for members.', $member, $moderator);
            $this->addNewsTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator);
            $this->addNewsTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin);

            $this->addGalleryTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member, $moderator);
            $this->addGalleryTemplate($site, 'members only', 'Content is accessible for members.', $member, $member, $moderator);
            $this->addGalleryTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator, $moderator);
            $this->addGalleryTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin, $admin);

            $this->addForumTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member, $member, $moderator);
            $this->addForumTemplate($site, 'members only', 'Content is accessible for members.', $member, $member, $member, $moderator);
            $this->addForumTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator, $moderator, $moderator);
            $this->addForumTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin, $admin, $admin);

            $this->addEventTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member);
            $this->addEventTemplate($site, 'members only', 'Content is accessible for members.', $member, $member);
            $this->addEventTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator);
            $this->addEventTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin);

            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }

        return $site;
    }

    public function createGame(IGameData $gameData, $name)
    {
        try {
            $this->beginTransaction();
            $site = $this->gameService->createGame($this->getLoggedInUser(), $name, $gameData);

            $guest = $this->permissionReader->findGuestRole();
            $member = $this->permissionWriter->createRole('Member', $site, $guest);
            $member->setAsDefaultRole();
            $moderator = $this->permissionWriter->createRole('Moderator', $site, $member);
            $admin = $this->permissionWriter->createRole('Admin', $site, $moderator);

            // templates wont have the same structure as portal site in the future
            $siteTemplate = $this->addSiteTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $admin, $moderator);
            $site->setTemplate($siteTemplate);

            $this->addNewsTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $moderator);
            $this->addNewsTemplate($site, 'members only', 'Content is accessible for members.', $member, $moderator);
            $this->addNewsTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator);
            $this->addNewsTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin);

            $this->addGalleryTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member, $moderator);
            $this->addGalleryTemplate($site, 'members only', 'Content is accessible for members.', $member, $member, $moderator);
            $this->addGalleryTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator, $moderator);
            $this->addGalleryTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin, $admin);

            $this->addForumTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member, $member, $moderator);
            $this->addForumTemplate($site, 'members only', 'Content is accessible for members.', $member, $member, $member, $moderator);
            $this->addForumTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator, $moderator, $moderator);
            $this->addForumTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin, $admin, $admin);

            $this->addEventTemplate($site, 'public', 'Content is accessible for everyone.', $guest, $member);
            $this->addEventTemplate($site, 'members only', 'Content is accessible for members.', $member, $member);
            $this->addEventTemplate($site, 'moderators only', 'Content is accessible for moderators.', $moderator, $moderator);
            $this->addEventTemplate($site, 'admins only', 'Content is accessible for admins.', $admin, $admin);

            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }

        return $site;
    }

    public function createGuild(string $name, $game, $server, $faction) : MmorpgGuild
    {
        $gameEntity = $this->gameReader->validateGame($game);
        $serverEntity = $this->serverReader->validateServer($server);
        $factionEntity = $this->factionReader->validateFaction($faction);

        try {
            $this->beginTransaction();
            $user = $this->getLoggedInUser();
            $guild = $this->siteService->createGuild($user, $name, $gameEntity, $serverEntity, $factionEntity);
            $guild->addMember($user);

            $guest = $this->permissionReader->findGuestRole();
            $member = $this->permissionWriter->createRole('Member', $guild, $guest);
            $member->setAsDefaultRole();
            $officer = $this->permissionWriter->createRole('Officer', $guild, $member);
            $gum = $this->permissionWriter->createRole('Guild master', $guild, $officer);

            $siteTemplate = $this->addSiteTemplate($guild, 'public', 'Content is accessible for everyone.', $guest, $gum, $officer);
            $guild->setTemplate($siteTemplate);

            $this->addNewsTemplate($guild, 'public', 'Content is accessible for everyone.', $guest, $officer);
            $this->addNewsTemplate($guild, 'members only', 'Content is accessible for members.', $member, $officer);
            $this->addNewsTemplate($guild, 'officers only', 'Content is accessible for officers.', $officer, $officer);

            $this->addGalleryTemplate($guild, 'public', 'Content is accessible for everyone.', $guest, $member, $officer);
            $this->addGalleryTemplate($guild, 'members only', 'Content is accessible for members.', $member, $member, $officer);
            $this->addGalleryTemplate($guild, 'officers only', 'Content is accessible for officers.', $officer, $officer, $officer);

            $this->addForumTemplate($guild, 'public', 'Content is accessible for everyone.', $guest, $member, $member, $officer);
            $this->addForumTemplate($guild, 'members only', 'Content is accessible for members.', $member, $member, $member, $officer);
            $this->addForumTemplate($guild, 'officers only', 'Content is accessible for officers.', $officer, $officer, $officer, $officer);

            $this->addEventTemplate($guild, 'public', 'Content is accessible for everyone.', $guest, $member);
            $this->addEventTemplate($guild, 'members only', 'Content is accessible for members.', $member, $member);
            $this->addEventTemplate($guild, 'officers only', 'Content is accessible for officers.', $officer, $officer);

            $user->addRole($gum);
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }

        return $guild;
    }

    protected function addSiteTemplate(ISite $site, string $name, string $description, Role $view, Role $addSection, Role $createEvent) : SiteTemplate
    {
        $siteTemplate = $this->templateWriter->createSiteTemplate($site, $name, $description);
        $siteTemplate->setState(ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($view, $siteTemplate, 'view', ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($addSection, $siteTemplate, 'add_section', ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($createEvent, $siteTemplate, 'create_event', ICommonRecord::ACTIVE);

        return $siteTemplate;
    }

    protected function addNewsTemplate(ISite $site, string $name, string $description, Role $view, Role $edit) : NewsTemplate
    {
        $newsTemplate = $this->templateWriter->createNewsTemplate($site, $name, $description);
        $newsTemplate->setState(ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($view, $newsTemplate, 'view', NewsContainer::ACTIVE);
        $this->permissionWriter->addRule($edit, $newsTemplate, 'edit', NewsContainer::ACTIVE);

        return $newsTemplate;
    }

    protected function addGalleryTemplate(ISite $site, string $name, string $description, Role $view, Role $upload, Role $edit) : GalleryTemplate
    {
        $galleryTemplate = $this->templateWriter->createGalleryTemplate($site, $name, $description);
        $galleryTemplate->setState(ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($view, $galleryTemplate, 'view', Gallery::ACTIVE);
        $this->permissionWriter->addRule($upload, $galleryTemplate, 'upload', Gallery::ACTIVE);
        $this->permissionWriter->addRule($edit, $galleryTemplate, 'edit', Gallery::ACTIVE);

        return $galleryTemplate;
    }

    protected function addForumTemplate(ISite $site, string $name, string $description, Role $view, Role $post, Role $createTopic, Role $createSubforum) : ForumTemplate
    {
        $forumTemplate = $this->templateWriter->createForumTemplate($site, $name, $description);
        $forumTemplate->setState(ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($view, $forumTemplate, 'view', Forum::ACTIVE);
        $this->permissionWriter->addRule($post, $forumTemplate, 'post', Forum::ACTIVE);
        $this->permissionWriter->addRule($createTopic, $forumTemplate, 'create_topic', Forum::ACTIVE);
        $this->permissionWriter->addRule($createSubforum, $forumTemplate, 'create_subforum', Forum::ACTIVE);

        return $forumTemplate;
    }

    protected function addEventTemplate(ISite $site, string $name, string $description, Role $view, Role $join) : EventTemplate
    {
        $eventTemplate = $this->templateWriter->createEventTemplate($site, $name, $description);
        $eventTemplate->setState(ICommonRecord::ACTIVE);
        $this->permissionWriter->addRule($view, $eventTemplate, 'view', Event::CREATED);
        $this->permissionWriter->addRule($join, $eventTemplate, 'join', Event::CREATED);

        return $eventTemplate;
    }
}