<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Service;

use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use MauticPlugin\DealFlowBundle\Entity\Deal;
use MauticPlugin\DealFlowBundle\Security\Permissions\DealFlowPermissions;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class DealAuthorization
{
    /**
     * @var array<string, bool>|null
     */
    private ?array $dealPermissions = null;

    /**
     * @var array<string, bool>|null
     */
    private ?array $reportPermissions = null;

    /**
     * @var array<string, bool>|null
     */
    private ?array $settingsPermissions = null;

    private bool $resolvedCurrentUserId = false;

    private ?int $currentUserId = null;

    public function __construct(
        private readonly CorePermissions $corePermissions,
        private readonly TokenStorageInterface $tokenStorage,
    ) {
    }

    public function canCreateDeals(): bool
    {
        return $this->getDealPermissions()[DealFlowPermissions::DEALS_CREATE];
    }

    public function canViewDeals(): bool
    {
        if ($this->canViewOtherDeals()) {
            return true;
        }

        return $this->getDealPermissions()[DealFlowPermissions::DEALS_VIEW_OWN] && null !== $this->getCurrentUserId();
    }

    public function canViewOtherDeals(): bool
    {
        return $this->getDealPermissions()[DealFlowPermissions::DEALS_VIEW_OTHER];
    }

    public function canViewDeal(Deal $deal): bool
    {
        if ($this->canViewOtherDeals()) {
            return true;
        }

        if (!$this->getDealPermissions()[DealFlowPermissions::DEALS_VIEW_OWN]) {
            return false;
        }

        return $this->isOwnedByCurrentUser($deal);
    }

    public function canEditDeal(Deal $deal): bool
    {
        if ($this->getDealPermissions()[DealFlowPermissions::DEALS_EDIT_OTHER]) {
            return true;
        }

        if (!$this->getDealPermissions()[DealFlowPermissions::DEALS_EDIT_OWN]) {
            return false;
        }

        return $this->isOwnedByCurrentUser($deal);
    }

    public function canViewReports(): bool
    {
        if ($this->canViewOtherReports()) {
            return true;
        }

        return $this->getReportPermissions()[DealFlowPermissions::REPORTS_VIEW_OWN] && null !== $this->getCurrentUserId();
    }

    public function canViewOtherReports(): bool
    {
        return $this->getReportPermissions()[DealFlowPermissions::REPORTS_VIEW_OTHER];
    }

    public function canViewSettings(): bool
    {
        $permissions = $this->getSettingsPermissions();

        return $permissions[DealFlowPermissions::SETTINGS_VIEW_OWN]
            || $permissions[DealFlowPermissions::SETTINGS_VIEW_OTHER]
            || $permissions[DealFlowPermissions::SETTINGS_EDIT_OWN]
            || $permissions[DealFlowPermissions::SETTINGS_EDIT_OTHER];
    }

    public function canDeleteDeal(Deal $deal): bool
    {
        if ($this->getDealPermissions()[DealFlowPermissions::DEALS_DELETE_OTHER]) {
            return true;
        }

        if (!$this->getDealPermissions()[DealFlowPermissions::DEALS_DELETE_OWN]) {
            return false;
        }

        return $this->isOwnedByCurrentUser($deal);
    }

    public function canEditSettings(): bool
    {
        $permissions = $this->getSettingsPermissions();

        return $permissions[DealFlowPermissions::SETTINGS_EDIT_OWN]
            || $permissions[DealFlowPermissions::SETTINGS_EDIT_OTHER];
    }

    public function getCurrentUserId(): ?int
    {
        if ($this->resolvedCurrentUserId) {
            return $this->currentUserId;
        }

        $this->resolvedCurrentUserId = true;
        $user = $this->tokenStorage->getToken()?->getUser();

        if (is_object($user) && method_exists($user, 'getId')) {
            $userId = $user->getId();
            if (is_numeric($userId)) {
                $this->currentUserId = (int) $userId;
            }
        }

        return $this->currentUserId;
    }

    public function isOwnedByCurrentUser(Deal $deal): bool
    {
        $currentUserId = $this->getCurrentUserId();
        if (null === $currentUserId) {
            return false;
        }

        if ($deal->getOwnerId() === $currentUserId) {
            return true;
        }

        return null === $deal->getOwnerId() && $deal->getCreatedBy() === $currentUserId;
    }

    /**
     * @return array<string, bool>
     */
    private function getDealPermissions(): array
    {
        if (null !== $this->dealPermissions) {
            return $this->dealPermissions;
        }

        return $this->dealPermissions = $this->corePermissions->isGranted([
            DealFlowPermissions::DEALS_VIEW_OWN,
            DealFlowPermissions::DEALS_VIEW_OTHER,
            DealFlowPermissions::DEALS_CREATE,
            DealFlowPermissions::DEALS_EDIT_OWN,
            DealFlowPermissions::DEALS_EDIT_OTHER,
            DealFlowPermissions::DEALS_DELETE_OWN,
            DealFlowPermissions::DEALS_DELETE_OTHER,
        ], 'RETURN_ARRAY');
    }

    /**
     * @return array<string, bool>
     */
    private function getReportPermissions(): array
    {
        if (null !== $this->reportPermissions) {
            return $this->reportPermissions;
        }

        return $this->reportPermissions = $this->corePermissions->isGranted([
            DealFlowPermissions::REPORTS_VIEW_OWN,
            DealFlowPermissions::REPORTS_VIEW_OTHER,
        ], 'RETURN_ARRAY');
    }

    /**
     * @return array<string, bool>
     */
    private function getSettingsPermissions(): array
    {
        if (null !== $this->settingsPermissions) {
            return $this->settingsPermissions;
        }

        return $this->settingsPermissions = $this->corePermissions->isGranted([
            DealFlowPermissions::SETTINGS_VIEW_OWN,
            DealFlowPermissions::SETTINGS_VIEW_OTHER,
            DealFlowPermissions::SETTINGS_EDIT_OWN,
            DealFlowPermissions::SETTINGS_EDIT_OTHER,
        ], 'RETURN_ARRAY');
    }
}
