<?php

namespace App\Builders;

use App\Models\Resource;
use Illuminate\Support\Collection;
use App\Services\DataConnectionService;
use App\Builders\Helpers\PaginationHelper;

class MetricDrillBuilder
{
    private $metric;
    private $connection;
    private $query;
    private $options;
    private $dataConnectionService;

    public function __construct(Resource $metric)
    {
        $this->metric                = $metric;
        $this->dataConnectionService = new DataConnectionService();
    }

    private function initializeConnection()
    {
        $this->query = (new DataConnectionService())
            ->getConnection(
                isset($this->metric->data_connection_id)
                ? $this->metric->data_connection_id
                : 'default'
            );

        return $this;

    }

    private function populateOptions()
    {
        $this->options = [
            'performance_data_table' => $this->metric->getResourceConfiguration('performance_data_table'),
            'calculation_column'     => $this->metric->getResourceConfiguration('calculation_column'),
            'function'               => $this->metric->getResourceConfiguration('function'),
            'metric_direction'       => $this->metric->getResourceConfiguration('metric_direction'),
            'filtration_column'      => $this->metric->getResourceConfiguration('filtration_column'),
            'filtration_value'       => $this->metric->getResourceConfiguration('filtration_value'),
            'date_filter_column'     => $this->metric->getResourceConfiguration('date_filter_column'),
            'date_range'             => $this->metric->getResourceConfiguration('date_range'),
            'display_format'         => $this->metric->getResourceConfiguration('display_format'),
            'custom_unit_input'      => $this->metric->getResourceConfiguration('custom_unit_input'),
            'compare_with'           => $this->metric->getResourceConfiguration('compare_with'),
            'target_value_input'     => $this->metric->getResourceConfiguration('target_value_input'),
            'metric_type'            => $this->metric->getResourceConfiguration('metric_type'),
            'metric_color'           => $this->metric->getResourceConfiguration('metric_color'),
            'drill_down_columns'     => $this->metric->getResourceConfiguration('drill_down_columns')
        ];

        return $this;
    }

    private function addPerformanceTable()
    {
        $this->query = $this->query->table(
            $this->options['performance_data_table']
        );

        return $this;
    }

    private function addAxis()
    {
        $this->query = $this->query->selectRaw(
            $this->getSelect()
        );
        return $this;
    }

    private function addDateFilters()
    {

        if ($this->options['date_range'] != "all_time") {
            $this->query = $this->query->whereBetween(
                $this->getDateFilterColumn(),
                $this->getDatesBasedOnDateRange()
            );

        }

        return $this;
    }

    private function addFilterColumn()
    {

        if (isset($this->options['filtration_column']) && !empty($this->options['filtration_column'])) {
            $this->query = $this->query->where(
                $this->getFilterColumn(), 'LIKE',
                "%" . $this->getFilterValue() . "%"
            );

        }

        return $this;
    }

    private function getDatesBasedOnDateRange()
    {

        if (!empty($this->options['date_range']) && $this->options['date_range'] != "all_time") {

            switch ($this->options['date_range']) {
                case 'this_3_months':
                    return [now()->subMonths(2)->startOfMonth()->toDateTimeString(), now()->endOfMonth()->toDateTimeString()];

                    break;
                case 'last_3_months':
                    return [now()->subMonths(3)->startOfMonth()->toDateTimeString(), now()->subMonth()->endOfMonth()->toDateTimeString()];

                    break;
                case 'this_month':
                    return [now()->firstOfMonth()->toDateTimeString(), now()->endOfMonth()->toDateTimeString()];

                    break;
                case 'last_30_days':
                    return [now()->subDays(30)->startOfDay()->toDateTimeString(), now()->subDay()->endOfDay()->toDateTimeString()];

                    break;
                case 'this_week':
                    return [now()->startOfWeek(getStartOfWeek())->toDateTimeString(), now()->endOfWeek(getEndOfWeek())->toDateTimeString()];

                    break;
                case 'last_7_days':
                    return [now()->subDays(7)->startOfDay()->toDateTimeString(), now()->subDay()->endOfDay()->toDateTimeString()];

                    break;
                case 'today':
                    return [now()->startOfDay()->toDateTimeString(), now()->endOfDay()->toDateTimeString()];

                    break;
                case 'yesterday':
                    return [now()->subDay()->startOfDay()->toDateTimeString(), now()->subDay()->endOfDay()->toDateTimeString()];
                    break;
                default:
                    break;
            }

        }

    }

    private function getSelect()
    {
        return implode(",", $this->getDrillColumns());
    }

    private function getDrillColumns()
    {
        $drillColumns = [];

        $drillColumns = array_map(function ($column) {
            return "`" . $this->options['performance_data_table'] . "`" . '.' . "`" . $column . "`";
        }, $this->options['drill_down_columns']);

        array_unshift($drillColumns, $this->getFirstColumn());

        return array_unique($drillColumns);

    }

    private function getDrillColumnsWithoutTable()
    {
        $drillColumns = [];

        $drillColumns = array_map(function ($column) {
            return $column;
        }, $this->options['drill_down_columns']);

        array_unshift($drillColumns, $this->options['calculation_column']);
        return array_unique($drillColumns);

    }

    private function getCalculationColumn($backTick = null)
    {

        if ($backTick) {
            return "`{$this->options['performance_data_table']}`.`{$this->options['calculation_column']}`";

        }

        return "{$this->options['performance_data_table']}.{$this->options['calculation_column']}";
    }

    private function getDateFilterColumn($backTick = null)
    {

        if ($backTick) {
            return "`{$this->options['performance_data_table']}`.`{$this->options['date_filter_column']}`";

        }

        return "{$this->options['performance_data_table']}.{$this->options['date_filter_column']}";
    }

    private function getFilterColumn()
    {

        return "{$this->options['performance_data_table']}.{$this->options['filtration_column']}";
    }

    private function getFilterValue()
    {

        return $this->options['filtration_value'] ?? "";
    }

    private function getFirstColumn()
    {
        return $this->getCalculationColumn(true);
    }

    private function getAxisValues($query)
    {

        if (!$query instanceof Collection) {
            return intval($query);
        }

        $axisValues = json_decode(json_encode($query), true);

        $originalMetric = $axisValues[0]['metric'];

        return $originalMetric;

    }

    private function buildOriginalMetric()
    {
        $this->initializeConnection()
            ->populateOptions()
            ->addPerformanceTable()
            ->addAxis()
            ->addDateFilters()
            ->addFilterColumn();

        return $this->query->get();

    }

    private function prepareData($data)
    {
        $xAxis = $this->options['calculation_column'];

        $drillMaxRecords = config('srm_config.dashboard.drill_down_max_records_per_page', 25);

        $sortedCollection = $data->map(function ($item) use ($xAxis) {

            ${$xAxis}

            = $item->$xAxis;
            unset($item->${$xAxis});
            return (object) array_merge(["$xAxis" => ${$xAxis}

            ], (array) $item);
        })->values();

        $keys = collect($this->getDrillColumnsWithoutTable());

        return [PaginationHelper::paginate($sortedCollection, $drillMaxRecords), $keys];

    }

    public function build()
    {
        $this->initializeConnection()
            ->populateOptions()
            ->addPerformanceTable()
            ->addAxis()
            ->addDateFilters()
            ->addFilterColumn();

        if ($this->options['compare_with'] != 'no_comparison') {
            $this->metric->script = (new MetricBuilder(
                $this->metric
            ))->build(true, "big");
        }

        return $this->prepareData($this->query->get());

    }

}
