<?php

namespace App\Builders\Helpers;

use Carbon\Carbon;

class InterpolateTimeSeries
{
    private $xAxis;
    private $yAxis;
    private $dateRange;
    private $timeScale;

    public function __construct($xAxis, $yAxis, $dateRange, $timeScale)
    {
        $this->xAxis     = $xAxis;
        $this->yAxis     = $yAxis;
        $this->dateRange = $dateRange;
        $this->timeScale = $timeScale;
    }

    public function interpolateTimeSeries()
    {

        switch ($this->timeScale) {
            case 'years':
                return $this->makeInterpolation(1, 'years');
                break;
            case 'months':
                return $this->makeInterpolation(1, 'months');
                break;
            case 'days':
                return $this->makeInterpolation(1, 'days');
                break;
            case 'hours':
                return $this->makeInterpolation(1, 'hours');
                break;
            case 'quarters':
                return $this->makeInterpolationQuarters();
                break;
            case 'weeks':
                return $this->makeInterpolationWeeks();
                break;
            default:
                return [
                    $this->xAxis,
                    $this->yAxis
                ];
                break;
        }

    }

    private function makeInterpolation($interval, $unit)
    {
        $data = $this->getAxisAsArrayOfObject($this->xAxis, $this->yAxis);

        if (count($data) > 0) {
            $format = 'Y-m-d';

            if ($unit == 'hours') {
                $format    = 'Y-m-d H:i:s';
                $startDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[0]);

                $endDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[1]);

            } else {
                $startDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[0]);

                $endDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[1]);

            }

            $interpolatedData = [];

            for ($date = $startDate->copy(); $date->lte($endDate); $date->add($unit, $interval)) {
                $dateStr = $date->format($format);

                if (array_key_exists($dateStr, $data)) {
                    $interpolatedData[$dateStr] = $data[$dateStr];
                } else {
                    $interpolatedData[$dateStr] = 0;
                }

            }

            return [
                array_keys($interpolatedData),
                array_values($interpolatedData)
            ];
        } else {
            return [
                $this->xAxis,
                $this->yAxis
            ];

        }

    }

    private function makeInterpolationWeeks()
    {
        $data = $this->getAxisAsArrayOfObject($this->xAxis, $this->yAxis);

        if (count($data) > 4) {
            unset($data[array_key_first($data)]);
        }

        if (count($data) > 0) {

            $startDate = Carbon::createFromFormat('Y-m-d', array_key_first($data));

            if (!$startDate->isSunday() || $startDate < Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[0])) {
                $startDate->next(Carbon::SUNDAY);
            }

            $endDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[1])->endOfWeek(Carbon::SATURDAY);

            $interpolatedData = [];

            // Get the start and end dates of the first and last week
            $startOfWeek = $startDate->format('Y-m-d');
            $endOfWeek   = $endDate->format('Y-m-d');

            $currentDate = $startOfWeek;

            while ($currentDate <= $endOfWeek) {

// If the week is within the date range, interpolate

                if (Carbon::createFromFormat('Y-m-d', $currentDate)->between($startDate, $endDate)) {
                    $interpolatedData[$currentDate] = array_key_exists($currentDate, $data) ? $data[$currentDate] : 0;
                }

                // Move to the next week
                $currentDate = Carbon::createFromFormat('Y-m-d', $currentDate)->addWeek()->format('Y-m-d');
            }

            return [
                array_keys($interpolatedData),
                array_values($interpolatedData)
            ];

        } else {
            return [
                $this->xAxis,
                $this->yAxis
            ];

        }

    }

    private function makeInterpolationQuarters()
    {
        $data = $this->getAxisAsArrayOfObject($this->xAxis, $this->yAxis);

        if (count($data) > 0) {

            $startDate = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[0]);

            $endDate          = Carbon::createFromFormat('Y-m-d H:i:s', $this->dateRange[1]);
            $interpolatedData = [];

            $quarters = ['01', '04', '07', '10'];

            for ($year = $startDate->year; $year <= $endDate->year; $year++) {

                foreach ($quarters as $quarter) {
                    $quarterStart = $year . '-' . $quarter . '-01';
                    $quarterEnd   = Carbon::createFromFormat('Y-m-d', $year . '-' . $quarter . '-01')->endOfMonth()->format('Y-m-d');

                    if (Carbon::createFromFormat('Y-m-d', $quarterStart)->between($startDate, $endDate)) {
                        $interpolatedData[$quarterStart] = array_key_exists($quarterStart, $data) ? $data[$quarterStart] : 0;
                    }

                }

            }

            return [
                array_keys($interpolatedData),
                array_values($interpolatedData)
            ];
        } else {
            return [
                $this->xAxis,
                $this->yAxis
            ];

        }

    }

    private function getAxisAsArrayOfObject($xAxis, $yAxis)
    {
        $result = array_map(function ($xAxis, $yAxis) {
            return [$xAxis => $yAxis];
        }, $xAxis, $yAxis);

        $output = [];

        foreach ($result as $item) {
            $output[key($item)] = current($item);
        }

        return $output;
    }

}
