<?php

namespace App\Http\Requests\Chart;

use Illuminate\Validation\Rule;
use App\Services\DataConnectionService;
use Illuminate\Foundation\Http\FormRequest;
use App\Services\DataConnectionTablesService;

class DataStepRequest extends FormRequest
{
    private $dataConnectionService;
    private $dataConnectionTablesService;
    protected $stopOnFirstFailure = true;

    public function __construct(DataConnectionService $dataConnectionService, DataConnectionTablesService $dataConnectionTablesService)
    {
        parent::__construct();
        $this->dataConnectionService       = $dataConnectionService;
        $this->dataConnectionTablesService = $dataConnectionTablesService;
    }

    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            "x_table"             => ["bail", "required"],
            "x_axis_column"       => ["required", function ($attribute, $value, $fail) {

                if (!is_null($this->input("x_table")) && !is_null($this->input('x_axis_column'))) {

                    $columnType = $this->dataConnectionTablesService->getConnectionTableColumnType(
                        $this->getValueFromChartSession("connection"),
                        $this->input("x_table"),
                        $this->input("x_axis_column")
                    );

                    if (
                        in_array($this->getValueFromChartSession("chart_type"), ["pie-chart", "donut-chart", "geo-chart"])
                        && in_array($columnType, ["datetime", "timestamp", "date"])
                    ) {
                        $fail(trans('charts.validations.required.choose a different chart type'));
                    }

                    if ($this->getValueFromChartSession("chart_type") == "timeseries"
                        && !(
                            in_array($columnType, ["datetime", "timestamp", "date"]) ||
                            $this->dataConnectionTablesService->checkIsDate(
                                $this->getConnectionFromSession(),
                                $this->input("x_table"),
                                $this->input("x_axis_column")
                            )
                        )) {
                        $fail(trans('charts.validations.custom.Although the format suggests'));
                    }

                }

            }

            ],
            "y_table"             => ["required"],
            "function"            => ["required", function ($attribute, $value, $fail) {

                if (!is_null($this->input("y_axis_column")) && !is_null($this->input('y_table'))) {

                    $columnName      = $this->input("y_axis_column");
                    $nonNumericCount = $this->getConnectionFromSession()->table($this->input('y_table'))
                        ->whereRaw("NOT {$columnName} REGEXP '^[-+]?([0-9]+\\.?[0-9]*|\\.[0-9]+)([eE][-+]?[0-9]+)?$'")
                        ->whereNotNull($columnName)
                        ->count();

                    if ($value != "count" && $nonNumericCount > 0) {
                        $fail(trans('charts.validations.required.The selected function can only be applied to numeric columns'));
                    }

                }

            }

            ],
            "drill_down"          => ["nullable"],
            "drill_down_columns"  => ["required_if:drill_down,on"],

            "date_range"          => ["bail", Rule::requiredIf(function () {

                if (!is_null($this->input("x_axis_column")) && !is_null($this->input('x_table'))) {
                    $connection      = $this->getValueFromChartSession("connection");
                    $xAxisColumnType = $this->dataConnectionTablesService->getColumnType($connection, $this->input("x_table"), $this->input("x_axis_column"));
                    return !in_array($xAxisColumnType, ["timestamp", "date", "datetime"]);
                }

            })],

            "date_time_scale"     => ["bail", Rule::requiredIf(function () {

                if (!is_null($this->input("x_axis_column")) && !is_null($this->input('x_table'))) {
                    $connection      = $this->getValueFromChartSession("connection");
                    $xAxisColumnType = $this->dataConnectionTablesService->getColumnType($connection, $this->input("x_table"), $this->input("x_axis_column"));
                    return !in_array($xAxisColumnType, ["timestamp", "date", "datetime"]);
                }

            })],

            "data_filter_columns" => ["array", Rule::requiredIf(function () {

                if (!is_null($this->input("x_axis_column")) && !is_null($this->input('x_table'))) {
                    $columnName      = $this->input("x_axis_column");
                    $connection      = $this->getValueFromChartSession("connection");
                    $xAxisColumnType = $this->dataConnectionTablesService->getColumnType($connection, $this->input("x_table"), $columnName);
                    $textualCount    = $this->getConnectionFromSession()->table($this->input('x_table'))
                        ->whereRaw("{$columnName} NOT REGEXP '^[0-9]+$'")
                        ->whereNotNull($columnName)
                        ->count();

                    if ($textualCount > 0 && !in_array($xAxisColumnType, ["timestamp", "date", "datetime"]) && $this->getValueFromChartSession('chart_type') !== "timeseries") {
                        return true;
                    }

                }

            })],

            "y_axis_column"       => ["required", function ($attribute, $value, $fail) {

                $xTable = $this->input('x_table');
                $yTable = $this->input('y_table');

                $foreignKeys = $this->getConnectionFromSession()->select("
                    SELECT
                        constraint_name as constraint_name,
                        column_name as column_name,
                        referenced_table_name as referenced_table_name,
                        referenced_column_name as referenced_column_name
                    FROM
                        information_schema.key_column_usage
                    WHERE
                        referenced_table_name IS NOT NULL
                        AND table_name = '{$xTable}'
                ");

                if ($xTable === $yTable) {
                    return;
                }

                foreach ($foreignKeys as $foreignKey) {

                    if ($foreignKey->referenced_table_name == $yTable) {
                        return;
                    }

                }

                $foreignKeys = $this->getConnectionFromSession()->select("
                    SELECT
                        constraint_name as constraint_name,
                        column_name as column_name,
                        referenced_table_name as referenced_table_name,
                        referenced_column_name as referenced_column_name
                    FROM
                        information_schema.key_column_usage
                    WHERE
                        referenced_table_name IS NOT NULL
                        AND table_name = '{$yTable}'
                ");

                foreach ($foreignKeys as $foreignKey) {

                    if ($foreignKey->referenced_table_name == $xTable) {
                        return;
                    }

                }

                $fail("Ensure you either choose the same table for both axes or select two related tables.");
            }

            ],
            "label"               => ["nullable"]

        ];
    }

    public function messages()
    {
        return [
            "x_table.required"               => trans('charts.validations.required.x_table'),
            "y_table.required"               => trans('charts.validations.required.x_table'),
            "x_axis_column.required"         => trans('charts.validations.required.axis_column'),
            "y_axis_column.required"         => trans('charts.validations.required.axis_column'),
            "drill_down_columns.required_if" => trans('charts.validations.required.drill_down_columns'),
            "data_filter_columns.required"   => trans('charts.validations.required.The chart will display as empty'),
            "date_time_scale.required_if"    => trans('charts.validations.required.date_time_scale')
        ];
    }

    protected function passedValidation()
    {

        if (!is_null($this->input("x_axis_column")) && !is_null($this->input('x_table'))) {
            $connection      = $this->getValueFromChartSession("connection");
            $xAxisColumnType = $this->dataConnectionTablesService->getColumnType($connection, $this->input("x_table"), $this->input("x_axis_column"));

            if ($this->getValueFromChartSession('widget_size') == "half"
                && (($this->input('date_time_scale') == "months" && in_array($this->input('date_range'), ["last_12_months", "this_year"]))
                    || ($this->input('date_time_scale') == "hours"))
                && in_array($xAxisColumnType, ["timestamp", "date", "datetime"])
            ) {

                $this->merge(
                    ['widget_size' => 'full']
                );

            } elseif ($this->getValueFromChartSession('widget_size') == "half"
                && $this->input('date_time_scale') == "days"
                && in_array($this->input('date_range'), ["this_month", "last_30_days"])
                && in_array($xAxisColumnType, ["timestamp", "date", "datetime"])

            ) {
                $this->setValueToChartSession('widget_size', 'full');

                $this->merge(
                    ['widget_size' => 'full']
                );

            }

        }

        if (
            !in_array($this->getValueFromChartSession("chart_type"), ["pie-chart", "donut-chart"]) &&
            $this->getValueFromChartSession('widget_size') == "half" &&
            (!is_null($this->input("data_filter_columns")) && count($this->input("data_filter_columns")) > 6)
        ) {
            $this->setValueToChartSession('widget_size', 'full');
            $this->merge(
                ['widget_size' => 'full']
            );

        }

    }

    public function withValidator($validator)
    {
        $validator->stopOnFirstFailure();
    }

    public function getConnectionFromSession()
    {
        $connection = session('chart_session_wizard_configuration')["connection"];
        return $this->dataConnectionService->getConnection($connection);

    }

    public function getValueFromChartSession($value)
    {
        return session('chart_session_wizard_configuration')[$value];

    }

    public function setValueToChartSession($key, $value)
    {

        if (!is_null(session()->get('chart_session_wizard_configuration'))) {
            session()->put('chart_session_wizard_configuration', [
                $key => $value
            ] + session()->get('chart_session_wizard_configuration'));
        } else {
            session()->put('chart_session_wizard_configuration', [
                $key => $value
            ]);
        }

    }

}