<?php namespace RVR\Controller;

use Container;
use DateTime;
use RVR\Finance\ExchangeRateCalculator;
use RVR\PersistentData\Model\Community;
use RVR\PersistentData\Model\CommunityMember;
use RVR\PersistentData\Model\Transaction;
use RVR\PersistentData\Model\User;
use RVR\Repository\CommunityMemberRepository;
use RVR\Repository\CommunityRepository;
use RVR\Repository\CurrencyRepository;
use RVR\Repository\TransactionRepository;
use RVR\Repository\EventRepository;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
use SokoWeb\Interfaces\Authorization\ISecured;
use SokoWeb\Interfaces\Response\IContent;
use SokoWeb\Response\HtmlContent;
use SokoWeb\Response\JsonContent;

class TransactionController implements IAuthenticationRequired, ISecured
{
    private CommunityRepository $communityRepository;

    private CommunityMemberRepository $communityMemberRepository;

    private CurrencyRepository $currencyRepository;

    private TransactionRepository $transactionRepository;

    private EventRepository $eventRepository;

    private ?Community $community;

    private ?CommunityMember $ownCommunityMember;

    public function __construct()
    {
        $this->communityRepository = new CommunityRepository();
        $this->communityMemberRepository = new CommunityMemberRepository();
        $this->currencyRepository = new CurrencyRepository();
        $this->transactionRepository = new TransactionRepository();
        $this->eventRepository = new EventRepository();
    }

    public function isAuthenticationRequired(): bool
    {
        return true;
    }

    public function authorize(): bool
    {
        $communitySlug = \Container::$request->query('communitySlug');
        $this->community = $this->communityRepository->getBySlug($communitySlug);
        if ($this->community === null) {
            return false;
        }

        /**
        * @var User $user
        */
        $user = \Container::$request->user();
        $this->ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($this->community, $user);
        if ($this->ownCommunityMember === null) {
            return false;
        }

        return true;
    }

    public function getTransactions(): IContent
    {
        Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']);
        $exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());

        $eventSlug = Container::$request->query('event');
        if ($eventSlug) {
            $event = $this->eventRepository->getBySlug($eventSlug);
        } else {
            $event = null;
        }

        $itemsPerPage = 50;
        $numberOfTransactions = $event ?
            $this->transactionRepository->countAllByEvent($event) :
            $this->transactionRepository->countAllByCommunity($this->community);
        $pages = ceil($numberOfTransactions / $itemsPerPage);
        $currentPage = Container::$request->query('page') ?: 0;
        $transactions = $event ?
            $this->transactionRepository->getPagedByEvent(
                $event,
                $currentPage * $itemsPerPage,
                $itemsPerPage,
                true,
                ['currency', 'payer_user', 'payee_user']
            ) :
            $this->transactionRepository->getPagedByCommunity(
                $this->community,
                $currentPage * $itemsPerPage,
                $itemsPerPage,
                true,
                ['event', 'currency', 'payer_user', 'payee_user']
            );

        return new HtmlContent('communities/transactions', [
            'community' => $this->community,
            'event' => $event,
            'exchangeRateCalculator' => $exchangeRateCalculator,
            'pages' => $pages,
            'currentPage' => $currentPage,
            'numberOfTransactions' => $numberOfTransactions,
            'transactions' => $transactions
        ]);
    }

    public function getTransactionEdit(): ?IContent
    {
        $transactionId = Container::$request->query('transactionId');
        if ($transactionId) {
            $transaction = $this->transactionRepository->getById($transactionId);
            if ($transaction === null) {
                return null;
            }
            Container::$persistentDataManager->loadRelationsFromDb($transaction, false, ['event']);
            $event = $transaction->getEvent();
        } else {
            $transaction = null;
            $eventSlug = Container::$request->query('event');
            if ($eventSlug) {
                $event = $this->eventRepository->getBySlug($eventSlug);
            } else {
                $event = null;
            }
        }

        return new HtmlContent('communities/transaction_edit', [
            'community' => $this->community,
            'transaction' => $transaction,
            'event' => $event,
            'members' => $this->getMembers($this->community),
            'currencies' => $this->getCurrencies($this->community)
        ]);
    }

    public function saveTransaction(): ?IContent
    {
        $transactionId = Container::$request->query('transactionId');
        if ($transactionId) {
            $transaction = $this->transactionRepository->getById($transactionId);
        } else {
            $transaction = new Transaction();
            $transaction->setCommunity($this->community);
        }

        $transaction->setEventId(Container::$request->post('event_id') ?: null);
        $transaction->setCurrencyId(Container::$request->post('currency_id'));
        $transaction->setPayerUserId(Container::$request->post('payer_user_id'));
        $transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null);
        $transaction->setDescription(Container::$request->post('description'));
        $transaction->setSum(Container::$request->post('sum'));
        $transaction->setTimeDate(new DateTime(Container::$request->post('time')));
        Container::$persistentDataManager->saveToDb($transaction);

        return new JsonContent(['success' => true]);
    }

    public function deleteTransaction(): IContent
    {
        $transaction = $this->transactionRepository->getById(Container::$request->query('transactionId'));
        Container::$persistentDataManager->deleteFromDb($transaction);

        return new JsonContent(['success' => true]);
    }

    private function getMembers(Community $community): array
    {
        $members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user']));
        usort($members, function ($a, $b) {
            return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
        });
        return $members;
    }

    private function getCurrencies(Community $community): array
    {
        $currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community));
        usort($currencies, function ($a, $b) {
            return strnatcmp($a->getCode(), $b->getCode());
        });
        usort($currencies, function ($a, $b) use ($community) {
            return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId());
        });
        return $currencies;
    }
}