<?php

namespace App\Generators;

use App\Models\Resource;
use Illuminate\Support\Str;

class GaugeChartScriptGenerator
{
    private $metric;
    private $originalMetric;
    private $comparisonMetric;
    private $change;
    private $trend;
    private $trendDirection;

    public function __construct(Resource $metric, $data)
    {
        $this->metric           = $metric;
        $this->originalMetric   = $data[0];
        $this->comparisonMetric = $data[1];
        $this->change           = $data[2];
        $this->trend            = $data[3];
        $this->trendDirection   = $data[4];
    }

    private function tooltip()
    {
        $original = $this->displayFormat($this->originalMetric);
        $tooltip  = " <span class='tooltip-bold'>Performance comparison:</span><br>";
        $tooltip .= !in_array($this->metric->getResourceConfiguration('display_format'), ["percentage_of_total", "percentage"])
        ? $this->displayFormat($this->change) : "";

        $tooltip = $this->trendDirection == "positive" ?
        $tooltip . " <span class='tooltip-green'>(" . $this->trend . "%)</span>  more than "
        : $tooltip . " <span class='tooltip-red'>(" . $this->trend . "%)</span>  less than ";

        switch ($this->metric->getResourceConfiguration('compare_with')) {
            case 'compare_with_same_time_last_year':
                return $tooltip . "the ​same time last year";
                break;
            case 'compare_with_preceding_period':
                return $tooltip . "the preceding period";
                break;
            case 'compare_with_a_fixed_target_value':
                return $tooltip . "the target value of " . $this->displayFormat($this->metric->getResourceConfiguration('target_value_input'));
                break;
            default:
                return;
                break;
        }

        return $tooltip;
    }

    private function getDisplayUnit()
    {

        switch ($this->metric->getResourceConfiguration('display_format')) {
            case 'numeric_with_custom_unit':
            case 'compact_currency':
            case 'standard_currency':
                return "{$this->metric->getResourceConfiguration('custom_unit_input')}";
                break;
            case 'percentage':
            case 'percentage_of_total':
                return "%";
                break;
            default:
                break;
        }

    }

    private function displayFormat($number)
    {

        switch ($this->metric->getResourceConfiguration('display_format')) {
            case 'numeric':
            case 'percentage':
            case 'percentage_of_total':
                return $number;
            case 'numeric_with_custom_unit':
                return $number . " " . $this->getDisplayUnit();
            case 'compact_currency':
                return formatCurrencyCompact($number, $this->metric->getResourceConfiguration('custom_unit_input'));
            case 'standard_currency':
                return formatStandardCurrency($number, $this->metric->getResourceConfiguration('custom_unit_input'));
            default:
                break;
        }

    }

    public function getGaugeScript($size = "small")
    {

        $tooltipText = $this->tooltip();

        [$name, $metricName] = $this->getGaugeOptions();
        return <<<END
        var $name =  c3.generate({
            bindto: "#$metricName",
            oninit: function(){
                {$this->getTrendAndChange($metricName,$size)}
                {$this->formatMaxAndMin($metricName)}
            },
            onrendered: function () {
                {$this->formatMaxAndMin($metricName)}

                const tooltipContainer = d3.select('#$metricName .c3-tooltip-container');
                tooltipContainer.classed('c3-tooltip-container', false);
                tooltipContainer.classed('gauge-chart-tooltip-container', true);

            },
            data: {
                columns: [["data", {$this->getData()}]],
                type: "gauge",
            },
            size: {
                height: {$this->getHeight($size)},
            },
            gauge:{
                max:{$this->getMax()},
                {$this->getLabel()}
            },
            legend:{
                show:false
            },
            color: {
                pattern: ["{$this->getGaugeColor()} "],
            },
            tooltip: {
                {$this->showToolTip()}
                contents: function (d, defaultTitleFormat, defaultValueFormat, color) {

                var dataPoint = d[0];

                var html = '<div class="gauge-tooltip" role="tooltip">' +
                        '<div class="tooltip-arrow"></div>' +
                        '<div class="tooltip-inner">' +
                        "$tooltipText" +
                        '</div>' +
                        '</div>';
                return html;

            }

            }
        });
        {$this->setHideTooltipEffect($metricName)}
        END;

    }

    private function showToolTip()
    {

        if ($this->getOption('compare_with') == "no_comparison") {
            return <<<END
                show: false,
            END;
        } else {
            return <<<END
                show: true,
            END;
        }

    }

    private function getGaugeOptions()
    {
        $name       = Str::of($this->getOption('name'))->camel();
        $metricName = Str::of($this->getOption('name'))->kebab();
        return [$name, $metricName];
    }

    private function getOption($option)
    {
        return $this->metric->getResourceConfiguration($option);
    }

    private function getData()
    {

        if ($this->trend === '∞') {
            return $this->getOption('compare_with') != "no_comparison" ? 100 : round($this->originalMetric, 1);
        }

        return is_numeric($this->trend) ? round($this->trend, 1) : 0;

    }

    private function getTrendAsPercentage()
    {

        if (!is_numeric($this->trend)) {
            return $this->trend . "%";
        }

        if ($this->trendDirection == "positive") {
            return "↑" . round($this->trend, 1) . "%";
        } else {
            return "↓" . round($this->trend, 1) . "%";
        }

    }

    private function getGaugeColor()
    {
        return $this->trendDirection == "positive" ? "#28a745" : "#dc3545";
    }

    private function getTrendAndChange($metricName, $size = "small")
    {
        $change = !in_array($this->metric->getResourceConfiguration('display_format'), ["percentage_of_total", "percentage"])
        ? $this->change : "";

        if ($this->getOption('compare_with') != "no_comparison") {
            $options = $this->getTrendAndChangeOptions($size);
            return <<<END
            d3.select("#$metricName")
                .select("svg")
                .select(".c3-chart-arcs")
                .select(".c3-chart-arc")
                .append("text")
                .attr("dy", "{$options['dy']}")
                .attr("dx", "{$options['dx']}")
                .style("text-anchor", "middle")
                .style("font-size", "{$options['font-size']}")
                .style("fill", "#000")
                .text("{$change} ({$this->getTrendAsPercentage()})");
        END;
        }

    }

    private function getTrendAndChangeOptions($size = "small")
    {

        switch ($size) {
            case 'small':
                return ["dy" => "-2em", "dx" => "0.1em", "font-size" => "9px"];
                break;
            case 'big':
                return ["dy" => "-3em", "dx" => "0.4em", "font-size" => "16px"];
                break;
            default:
                break;
        }

    }

    private function getMax()
    {
        return 100;
    }

    private function getLabel()
    {

        if ($this->getOption('display_format') == "numeric") {
            return <<<END
                label: {
                    format: function (value, ratio) {
                        return {$this->getOriginalMetric()};
                    },
                }
            END;
        } elseif ($this->getOption('display_format') == "numeric_with_custom_unit") {
            return <<<END
                label: {
                    format: function (value, ratio) {
                        return '{$this->getOriginalMetric()} {$this->getCustomUnit()}';
                    },
                }
            END;
        } elseif ($this->getOption('display_format') == "percentage") {
            return <<<END
                label: {
                    format: function (value, ratio) {
                        return {$this->getOriginalMetric()} + '%';
                    }
                },
            END;
        } elseif ($this->getOption('display_format') == "compact_currency") {
            $result = formatCurrencyCompact($this->getOriginalMetric(), $this->metric->getResourceConfiguration('custom_unit_input'));
            return <<<END
            label: {
                format: function (value, ratio) {
                    return "$result";
                }
            },
        END;

        } elseif ($this->getOption('display_format') == "standard_currency") {
            $result = formatStandardCurrency($this->getOriginalMetric(), $this->metric->getResourceConfiguration('custom_unit_input'));
            return <<<END
            label: {
                format: function (value, ratio) {
                    return "$result";
                }
            },
        END;

        } else {
            return <<<END
            label: {
                format: function (value, ratio) {
                        return {$this->getOriginalMetric()} + '%';
                }
            },
        END;

        }

    }

    private function formatMaxAndMin($metricName)
    {

        return <<<END
            d3.select("#$metricName").select(".c3-chart-arcs-gauge-max").text("{$this->getMax()}%");
            d3.select("#$metricName").select(".c3-chart-arcs-gauge-min").text("0%");
        END;

    }

    private function getCustomUnit()
    {
        return substr(
            $this->getOption('custom_unit_input'),
            0,
            5
        );
    }

    private function getOriginalMetric()
    {
        return round($this->originalMetric, 1);
    }

    private function getHeight($size)
    {

        switch ($size) {
            case 'big':
                return 300;
                break;
            case 'small':
                return 104;
                break;
        }

    }

    private function setHideTooltipEffect($metricName)
    {
        $name = Str::of($metricName)->camel();

        return <<<END
            c3.chart.internal.fn.hideTooltip = function (){};
            {$name}.{$name}TooltipTimeout = null;

            function hideTooltip(metricName) {
                d3.select("#" + metricName + " .gauge-chart-tooltip-container").style("display", "none");
            }

            d3.select("#" + "$metricName" + " .gauge-chart-tooltip-container")
            .on("mouseenter", function () {
                clearTimeout({$name}.{$name}TooltipTimeout);
            }).on("mouseleave", function () {
                {$name}.{$name}TooltipTimeout = setTimeout(function () {
                    hideTooltip("$metricName");
                }, 200);
            });

            d3.select("#" + "$metricName" + " .c3-chart")
            .on("mouseenter", function () {
                clearTimeout({$name}.{$name}TooltipTimeout);
            }).on("mouseleave", function () {
                {$name}.{$name}TooltipTimeout = setTimeout(function () {
                    hideTooltip("$metricName");
                }, 200);
            });
        END;
    }

}
