<?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 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 Community $community;

    private CommunityMember $ownCommunityMember;

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

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

    public function authorize(): bool
    {
        $communityId = \Container::$request->query('communityId');
        $this->community = $this->communityRepository->getById($communityId);
        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());

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

        return new HtmlContent('communities/transactions', [
            'community' => $this->community,
            '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;
            }
        } else {
            $transaction = null;
        }

        return new HtmlContent('communities/transaction_edit', [
            'community' => $this->community,
            'transaction' => $transaction,
            '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->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;
    }
}