<?php
namespace Incoviba\Controller\API;

use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
use Incoviba\Common\Implement\Exception\EmptyRedis;
use Incoviba\Controller\withRedis;
use Incoviba\Service;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;

class Money
{
    use withJson, withRedis;

    private int $time = 60 * 60 * 24 * 30;

    public function get(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                        Service\Money $moneyService): ResponseInterface
    {
        $data = $request->getParsedBody();
        $output = [
            'input' => $data
        ];
        $output[$data['provider']] = 0;
        $redisKey = $data['provider'];
        $date = new DateTimeImmutable($data['fecha']);
        if (isset($data['start'])) {
            $start = new DateTimeImmutable($data['start']);
        }
        $value = $this->getValue($redisService, $redisKey, $moneyService, $date, $data['provider']);
        if (isset($start)) {
            $months = $date->diff($start)->m;
            $current = clone $start;
            $value = $this->getValue($redisService, $redisKey, $moneyService, $current, $data['provider']);
            for ($i = 1; $i <= $months; $i ++) {
                $current = $current->add(new DateInterval("P{$i}M"));
                $value += $this->getValue($redisService, $redisKey, $moneyService, $current, $data['provider']);
            }
        }
        $output[$data['provider']] = $value;
        return $this->withJson($response, $output);
    }

    protected array $data;
    protected function getValue(Service\Redis $redisService, string $redisKey, Service\Money $moneyService,
                                DateTimeInterface $date, string $provider): float
    {
        if (isset($this->data[$provider][$date->format('Y-m-d')])) {
            return $this->data[$provider][$date->format('Y-m-d')];
        }
        try {
            $this->data[$provider] = (array) $this->fetchRedis($redisService, $redisKey);
            if (!isset($this->data[$provider][$date->format('Y-m-d')])) {
                throw new EmptyRedis($redisKey);
            }
        } catch (EmptyRedis) {
            $result = $moneyService->get($provider, $date);
            $this->data[$provider][$date->format('Y-m-d')] = $result;
            $this->saveRedis($redisService, $redisKey, $this->data[$provider], $this->time);
        }
        return $this->data[$provider][$date->format('Y-m-d')];
    }
    public function uf(ServerRequestInterface $request, ResponseInterface $response, Service\UF $ufService): ResponseInterface
    {
        $body = $request->getParsedBody();
        $output = [
            'input' => $body,
            'uf' => 0
        ];
        $date = new DateTimeImmutable($body['fecha']);
        $now = new DateTimeImmutable();
        if ($date > $now) {
            return $this->withJson($response, $output);
        }
        $output['uf'] = $ufService->get($date);
        return $this->withJson($response, $output);
    }
    public function ufs(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService): ResponseInterface
    {
        $redisKey = 'uf';
        $output = [
            'ufs' => []
        ];
        try {
            $output['ufs'] = (array) $this->fetchRedis($redisService, $redisKey);
        } catch (EmptyRedis) {}
        return $this->withJson($response, $output);
    }
    public function updateUfs(ServerRequestInterface $request, ResponseInterface $response,
                              LoggerInterface $logger,
                              Service\UF $ufService): ResponseInterface
    {
        $body = $request->getParsedBody();
        $dates = array_map(function($dateData) {
            return new DateTimeImmutable($dateData);
        }, $body['fechas']);
        $output = [
            'input' => $body,
            'ufs' => $ufService->updateMany($dates)
        ];
        return $this->withJson($response, $output);
    }
    public function ipc(ServerRequestInterface $request, ResponseInterface $response,
                        Service\IPC $ipcService): ResponseInterface
    {
        $data = $request->getParsedBody();
        $output = [
            'input' => $data,
            'date_string' => '',
            'ipc' => 0
        ];
        $start = new DateTimeImmutable($data['start']);
        $end = new DateTimeImmutable($data['end']);
        $now = new DateTimeImmutable();
        if ($end > $now) {
            return $this->withJson($response, $output);
        }
        $dateKey = "{$start->format('Y-m')}|{$end->format('Y-m')}";
        $output['date_string'] = $dateKey;
        $output['ipc'] = $ipcService->get($start, $end);
        return  $this->withJson($response, $output);
    }
    public function getMany(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                            Service\Money $moneyService): ResponseInterface
    {
        $input = $request->getParsedBody();
        $output = [
            'input' => $input,
            'values' => []
        ];
        $data = json_decode($input['dates']);
        $ufs = [];
        $ipcs = [];
        try {
            $ufs = (array) $this->fetchRedis($redisService, 'ufs');
        } catch (EmptyRedis) {}
        try {
            $ipcs = (array) $this->fetchRedis($redisService, 'ipcs');
        } catch (EmptyRedis) {}
        foreach ($data as $date) {
            if ($date->type === 'uf') {
                if (!in_array($date->date, $ufs)) {
                    $uf = $moneyService->getUF(new DateTimeImmutable($date->date));
                    $ufs[$date->date] = $uf;
                }
                $date->value = $ufs[$date->date];
                $output['values'] []= $date;
                continue;
            }
            if ($date->type === 'ipc') {
                $dateString = "{$date->start}|{$date->end}";
                if (!in_array($dateString, $ipcs)) {
                    $ipc = $moneyService->getIPC(new DateTimeImmutable($date->start), new DateTimeImmutable($date->end));
                    $ipcs[$dateString] = $ipc;
                }
                $date->value = $ipcs[$dateString];
                $output['values'] []= $date;
            }
        }
        $this->saveRedis($redisService, 'ufs', $ufs, $this->time);
        $this->saveRedis($redisService, 'ipcs', $ipcs, $this->time);
        return $this->withJson($response, $output);
    }
}
