<?php

/**
 * IndexController.php
 * Copyright (c) 2020 james@firefly-iii.org
 *
 * This file is part of Firefly III (https://github.com/firefly-iii).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

declare(strict_types=1);

namespace FireflyIII\Http\Controllers\PiggyBank;

use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\PiggyBankEnrichment;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\ParameterBag;

/**
 * Class IndexController
 */
final class IndexController extends Controller
{
    use OrganisesObjectGroups;

    private PiggyBankRepositoryInterface $piggyRepos;

    /**
     * PiggyBankController constructor.
     */
    public function __construct()
    {
        parent::__construct();

        $this->middleware(function ($request, $next) {
            app('view')->share('title', (string) trans('firefly.piggyBanks'));
            app('view')->share('mainTitleIcon', 'fa-bullseye');

            $this->piggyRepos = app(PiggyBankRepositoryInterface::class);

            return $next($request);
        });
    }

    /**
     * Show overview of all piggy banks.
     *
     * TODO very complicated function.
     *
     * @return Factory|View
     */
    public function index(): Factory|\Illuminate\Contracts\View\View
    {
        $this->cleanupObjectGroups();
        $this->piggyRepos->resetOrder();
        $collection = $this->piggyRepos->getPiggyBanks();

        // data
        $piggyBanks = $this->groupPiggyBanks($collection);
        $accounts   = $this->collectAccounts($collection);
        $accounts   = $this->mergeAccountsAndPiggies($piggyBanks, $accounts);
        $piggyBanks = $this->makeSums($piggyBanks);

        ksort($piggyBanks);

        return view('piggy-banks.index', ['piggyBanks' => $piggyBanks, 'accounts'   => $accounts]);
    }

    /**
     * Set the order of a piggy bank.
     */
    public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
    {
        $objectGroupTitle = (string) $request->input('objectGroupTitle');
        $newOrder         = (int) $request->input('order');
        $this->piggyRepos->setOrder($piggyBank, $newOrder);
        if ('' !== $objectGroupTitle) {
            $this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle);
        }
        if ('' === $objectGroupTitle) {
            $this->piggyRepos->removeObjectGroup($piggyBank);
        }

        return response()->json(['data' => 'OK']);
    }

    private function collectAccounts(Collection $collection): array
    {
        $now                = Carbon::now();

        /** @var AccountTransformer $accountTransformer */
        $accountTransformer = app(AccountTransformer::class);

        // enrich each account.
        $enrichment         = new AccountEnrichment();
        $enrichment->setUser(auth()->user());
        $enrichment->setDate($now);
        $return             = [];

        /** @var PiggyBank $piggy */
        foreach ($collection as $piggy) {
            $accounts = $piggy->accounts;

            /** @var Account $account */
            foreach ($accounts as $account) {
                /** @var Account $account */
                $account   = $enrichment->enrichSingle($account);
                $array     = $accountTransformer->transform($account);
                $accountId = (int) $array['id'];
                if (!array_key_exists($accountId, $return)) {
                    $return[$accountId]            = $array;

                    // add some interesting details:
                    $return[$accountId]['left']    = $return[$accountId]['current_balance'];
                    $return[$accountId]['saved']   = '0';
                    $return[$accountId]['target']  = '0';
                    $return[$accountId]['to_save'] = '0';
                }
            }
        }

        return $return;
    }

    private function groupPiggyBanks(Collection $collection): array
    {
        /** @var PiggyBankTransformer $transformer */
        $transformer = app(PiggyBankTransformer::class);
        $transformer->setParameters(new ParameterBag());
        $piggyBanks  = [];

        // enrich
        /** @var User $admin */
        $admin       = auth()->user();
        $enrichment  = new PiggyBankEnrichment();
        $enrichment->setUser($admin);
        $collection  = $enrichment->enrich($collection);

        /** @var PiggyBank $piggy */
        foreach ($collection as $piggy) {
            $array                                    = $transformer->transform($piggy);
            $groupOrder                               = (int) $array['object_group_order'];
            $piggyBanks[$groupOrder] ??= [
                'object_group_id'    => $array['object_group_id'] ?? 0,
                'object_group_title' => $array['object_group_title'] ?? trans('firefly.default_group_title_name'),
                'piggy_banks'        => [],
            ];
            $array['attachments']                     = $this->piggyRepos->getAttachments($piggy);

            // sum the total amount for the index.
            $piggyBanks[$groupOrder]['piggy_banks'][] = $array;
        }

        return $piggyBanks;
    }

    private function makeSums(array $piggyBanks): array
    {
        $sums = [];
        foreach ($piggyBanks as $groupOrder => $group) {
            $groupId = $group['object_group_id'];
            foreach ($group['piggy_banks'] as $piggy) {
                $currencyId                                    = $piggy['currency_id'];
                $sums[$groupId][$currencyId] ??= [
                    'target'                  => '0',
                    'saved'                   => '0',
                    'left_to_save'            => '0',
                    'save_per_month'          => '0',
                    'currency_id'             => $currencyId,
                    'currency_code'           => $piggy['currency_code'],
                    'currency_symbol'         => $piggy['currency_symbol'],
                    'currency_decimal_places' => $piggy['currency_decimal_places'],
                ];
                // target_amount
                // current_amount
                // left_to_save
                // save_per_month
                $sums[$groupId][$currencyId]['target']         = bcadd($sums[$groupId][$currencyId]['target'], (string) $piggy['target_amount']);
                $sums[$groupId][$currencyId]['saved']          = bcadd($sums[$groupId][$currencyId]['saved'], (string) $piggy['current_amount']);
                $sums[$groupId][$currencyId]['left_to_save']   = bcadd($sums[$groupId][$currencyId]['left_to_save'], (string) $piggy['left_to_save']);
                $sums[$groupId][$currencyId]['save_per_month'] = bcadd($sums[$groupId][$currencyId]['save_per_month'], (string) $piggy['save_per_month']);
            }
        }
        foreach ($piggyBanks as $groupOrder => $group) {
            $groupId                         = $group['object_group_id'];
            $piggyBanks[$groupOrder]['sums'] = $sums[$groupId] ?? [];
        }

        return $piggyBanks;
    }

    private function mergeAccountsAndPiggies(array $piggyBanks, array $accounts): array
    {
        /** @var array $group */
        foreach ($piggyBanks as $group) {
            /** @var array $piggyBank */
            foreach ($group['piggy_banks'] as $piggyBank) {
                // loop all accounts in this piggy bank subtract the current amount from "left to save" in the $accounts array.
                /** @var array $piggyAccount */
                foreach ($piggyBank['accounts'] as $piggyAccount) {
                    $accountId = $piggyAccount['account_id'];
                    if (array_key_exists($accountId, $accounts)) {
                        $accounts[$accountId]['left']    = bcsub((string) $accounts[$accountId]['left'], (string) $piggyAccount['current_amount']);
                        $accounts[$accountId]['saved']   = bcadd((string) $accounts[$accountId]['saved'], (string) $piggyAccount['current_amount']);
                        $accounts[$accountId]['target']  = bcadd((string) $accounts[$accountId]['target'], (string) $piggyBank['target_amount']);
                        $accounts[$accountId]['to_save'] = bcadd((string) $accounts[$accountId]['to_save'], bcsub(
                            (string) $piggyBank['target_amount'],
                            (string) $piggyAccount['current_amount']
                        ));
                    }
                }
            }
        }

        return $accounts;
    }
}
