<?php

namespace App\Services;

use App\Models\Resource;
use App\Builders\MetricBuilder;
use App\Services\ModuleService;
use App\Services\ReportService;
use App\Repositories\MetricRepository;

class MetricService
{
    public $groupService;
    public $reportSerivce;
    public $metricBuilder;
    public $metricRepository;
    public $moduleService;
    public $categoryService;
    public $metricDrillService;
    public $metricDetailsService;
    public $dataConnectionService;
    public $dataConnectionTablesService;
    public function __construct(
        ReportService $reportSerivce,
        MetricRepository $metricRepository,
        CategoryService $categoryService,
        MetricBuilder $metricBuilder,
        GroupService $groupService,
        MetricDrillService $metricDrillService,
        DataConnectionService $dataConnectionService,
        DataConnectionTablesService $dataConnectionTablesService,
        ModuleService $moduleService
    ) {
        $this->reportSerivce               = $reportSerivce;
        $this->metricRepository            = $metricRepository;
        $this->metricBuilder               = $metricBuilder;
        $this->categoryService             = $categoryService;
        $this->groupService                = $groupService;
        $this->metricDrillService          = $metricDrillService;
        $this->dataConnectionService       = $dataConnectionService;
        $this->dataConnectionTablesService = $dataConnectionTablesService;
        $this->moduleService               = $moduleService;
    }

    public function index($request)
    {
        $metricPerPage = $this->getMetricByPage();

        $metrics = $this->getMetrics($request)->paginate($metricPerPage);

        foreach ($metrics as $metric) {
            (new MetricBuilder($metric))->build();
        }

        return $metrics;
    }

    private function getMetricByPage()
    {
        return config('srm_config.dashboard.metrics_per_page', 40);
    }

    private function getNumberOfPages($request)
    {
        $metricPerPage = $this->getMetricByPage();
        return $this->getMetrics($request)->paginate($metricPerPage);
    }

    public function getMetricLastPage($request)
    {
        return $this->getNumberOfPages($request)->lastPage();
    }

    public function show($metric)
    {
        $metric = Resource::findResourceByName($metric)->firstOrFail();
        $this->checkMetricAccessPermission($metric);
        (new MetricBuilder($metric))->build();

        return $metric;

    }

    public function drill(Resource $metric)
    {
        $this->checkMetricAccessPermission($metric);
        return $this->metricDrillService->getData($metric);
    }

    public function edit(Resource $metric)
    {

        // $this->checkThereeIsGroupsAndCategories();

        $this->checkMetricCreatorPermission($metric);

        $connection = $metric->getConnectionAsString();

        $metricsTypes = $this->metricTypes();

        $categories = $this->categoryService->getCategories();

        $connections = $this->dataConnectionService->getAllConnections();

        $groups = auth()->user()->isAdminOrOwner() ? $this->groupService->getAllGroups() : [auth()->user()->group];

        $tables = $this->dataConnectionTablesService->getTables($connection);

        $columns = $this->dataConnectionTablesService->getColumns(
            $connection,
            $metric->getResourceConfiguration('performance_data_table')
        );

        $dateTimeColumns = $this->dataConnectionTablesService->getColumns(
            $connection,
            $metric->getResourceConfiguration('performance_data_table'),
            ["date", "datetime", "char", "varchar", "string", "timestamp"]
        );

        $dateRanges = $this->dateRanges();
        $colors     = $this->getMetricColors();

        return [
            $metricsTypes,
            $metric,
            $categories,
            $connections,
            $groups,
            $tables,
            $columns,
            $dateTimeColumns,
            $dateRanges,
            $colors
        ];

    }

    public function destroy($metric)
    {
        $this->checkMetricCreatorPermission($metric);

        $metric->homeWidgets()->delete();
        $metric->dashboardWidgets()->delete();
        $metric->delete();
    }

    public function getAllMetrics($request)
    {
        return Resource::when(
            (isset($request->metricTitle)),
            function ($q) use ($request) {
                $q->where('name', 'LIKE', '%' . $request->metricTitle . '%');

            }
        )->when(isset($request->metricCategory), function ($q) use ($request) {
            $q->whereHas('configurations', function ($q) use ($request) {
                return $q->where('category_id', $request->metricCategory);
            });

        })->whereHas('module', function ($q) {

            return $q->where('name', 'KPIs / Metrics');
        })->orWhereHas('module.parent', function ($q) {
            return $q->where('name', 'KPIs / Metrics');
        })
            ->with('category')
            ->orderBy('created_at', 'asc');
    }

    public function getRecentMetric()
    {
        return Resource::whereHas('module', function ($q) {

            return $q->where('name', 'KPIs / Metrics');
        })->orWhereHas('module.parent', function ($q) {
            return $q->where('name', 'KPIs / Metrics');
        })
            ->with('category')
            ->orderBy('created_at', 'asc')
            ->first();
    }

    public function getAuthedUserMetrics($request)
    {
        $prefix = config("srm_config.installer.table_prefix", "srm_");

        return Resource::when(
            (isset($request->metricTitle)),
            function ($q) use ($request) {
                $q->where('name', 'LIKE', '%' . $request->metricTitle . '%');

            }
        )->when(isset($request->metricCategory), function ($q) use ($request) {
            $q->where('name', $request->metricCategory);

        })->where(function ($q) use ($prefix) {
            $q->where(function ($q) {
                $q->whereHas('module', function ($q) {
                    return $q->where('name', 'KPIs / Metrics');
                })->orWhereHas('module.parent', function ($q) {
                    return $q->where('name', 'KPIs / Metrics');
                });
            })->where(function ($q) use ($prefix) {
                $q->whereHas('resource_permissions', function ($q) use ($prefix) {
                    return $q->where($prefix . 'resource_permissions.group_id', auth()->user()->group_id);
                })->orWhere(function ($q) {
                    $q->where('access_control_type', 'Public');
                });
                // ->orWhere('resource_creator', auth()->id());
            });
        })
            ->with('category')
            ->orderBy('created_at', 'asc');
    }

    public function showMetricTypeStep()
    {
        $this->checkCanUserCreateMetric();
        // $this->checkThereIsGroupsAndCategories();
    }

    private function checkCanUserCreateMetric()
    {

        if (
            !$this->moduleService->checkCurrentUserGroupCanCreateModule('KPIs / Metrics')
            && !auth()->user()->isAdminOrOwner()
        ) {
            abort(403, trans('metrics.messages.permission_denied'));
        }

    }

    public function metricTypes()
    {
        return [
            ["image" => "info_box.png", "label" => "Info Box", "value" => "info_box"],
            ["image" => "info_box_with_icon.png", "label" => "Info Box With Icon", "value" => "info_box_with_icon"],
            ["image" => "gauge_chart.png", "label" => "Gauge Chart", "value" => "gauge_chart"]
        ];
    }

    public function dateRanges()
    {
        return [
            ["label" => "All time", "value" => "all_time"],
            ["label" => "This Month and The 2 Months Before", "value" => "this_3_months"],
            ["label" => "Last 3 Months", "value" => "last_3_months"],
            ["label" => "This Month", "value" => "this_month"],
            ["label" => "Last 30 Days", "value" => "last_30_days"],
            ["label" => "This Week", "value" => "this_week"],
            ["label" => "Last 7 Days", "value" => "last_7_days"],
            ["label" => "Today", "value" => "today"],
            ["label" => "Yesterday", "value" => "yesterday"]
        ];
    }

    public function getMetricColors()
    {
        return [
            "white",
            "blue",
            "orange",
            "red",
            "purple",
            "green"
        ];
    }

    public function timeScales()
    {
        return [
            ["label" => "Hours", "value" => "hours"],
            ["label" => "Days", "value" => "days"],
            ["label" => "Weeks", "value" => "weeks"],
            ["label" => "Months", "value" => "months"],
            ["label" => "Quarters", "value" => "quarters"],
            ["label" => "Years", "value" => "years"]
        ];
    }

// public function getTextualCount($connection, $table, $column)

// {

//     return $this->dataConnectionService->getConnection($connection)->table($table)

//     ->whereRaw("{$column} NOT REGEXP '^[0-9]+$'")

//     ->whereNotNull($column)

//     ->count();
    // }

    public function storeApperanceStep($request)
    {
        $data = $request->validated();

        if (!is_null(session()->get('metric_session_wizard_configuration'))) {

            session()->put('metric_session_wizard_configuration', [
                'metric_type'   => !is_null($data['metric_type']) ? $data['metric_type'] : "",
                'metric_color'  => !is_null($data['metric_color']) ? $data['metric_color'] : "",
                'info_box_icon' => $request->has('info_box_icon') ? $data['info_box_icon'] : ""
            ] + session()->get('metric_session_wizard_configuration'));

        } else {
            session()->put('metric_session_wizard_configuration', [
                'metric_type'   => !is_null($data['metric_type']) ? $data['metric_type'] : "",
                'metric_color'  => !is_null($data['metric_color']) ? $data['metric_color'] : "",
                'info_box_icon' => !is_null($data['info_box_icon']) ? $data['info_box_icon'] : ""
            ]);
        }

    }

    public function storeSettingStep($request)
    {
        $data = $request->validated();
        session()->put('metric_session_wizard_configuration', [
            'name'          => $data['name'],
            'title'         => $data['title'],
            'category'      => $data['category'],
            'connection'    => $data['connection'],
            'security_type' => $data['security_type'],
            'groups'        => $request->has('groups') && $data['security_type'] == "private" ? $data['groups'] : null
        ] + session()->get('metric_session_wizard_configuration'));
    }

    public function updateSettingStep($request, $metric)
    {
        $data = $request->validated();
        session()->put('metric_session_wizard_configuration', [
            'name'          => $data['name'],
            'title'         => $data['title'],
            'category'      => $data['category'],
            'connection'    => $metric->data_connection_id,
            'security_type' => $data['security_type'],
            'groups'        => $request->has('groups') && $data['security_type'] == "private" ? $data['groups'] : null
        ] + session()->get('metric_session_wizard_configuration'));
    }

    public function storeDataStep($request)
    {
        $data = $request->validated();

        session()->put('metric_session_wizard_configuration', [
            'performance_data_table' => $data['performance_data_table'],
            'calculation_column'     => $data['calculation_column'],
            'function'               => $data['function'],
            'metric_direction'       => $data['metric_direction'],
            'filtration_column'      => isset($data['filtration_column']) ? $data['filtration_column'] : "",
            'filtration_value'       => isset($data['filtration_value']) ? $data['filtration_value'] : "",
            'date_filter_column'     => isset($data['date_filter_column']) ? $data['date_filter_column'] : "",
            'date_range'             => $data['date_range'],
            'display_format'         => $data['display_format'],
            'custom_unit_input'      => isset($data['custom_unit_input']) ? $data['custom_unit_input'] : "",
            'compare_with'           => $data['compare_with'],
            'target_value_input'     => isset($data['target_value_input']) ? $data['target_value_input'] : "",
            'drill_down'             => isset($data['drill_down']) ? true : false,
            'drill_down_columns'     => isset($data['drill_down_columns']) ? $data['drill_down_columns'] : ""
        ] + session()->get('metric_session_wizard_configuration'));

        $this->metricRepository->store();
    }

    public function updateDataStep($request, $metric)
    {
        $data        = $request->validated();
        $sessionData = [
            'performance_data_table' => $data['performance_data_table'],
            'calculation_column'     => $data['calculation_column'],
            'function'               => $data['function'],
            'metric_direction'       => $data['metric_direction'],
            'filtration_column'      => isset($data['filtration_column']) ? $data['filtration_column'] : "",
            'filtration_value'       => isset($data['filtration_value']) ? $data['filtration_value'] : "",
            'date_filter_column'     => isset($data['date_filter_column']) ? $data['date_filter_column'] : "",
            'date_range'             => $data['date_range'],
            'display_format'         => $data['display_format'],
            'custom_unit_input'      => isset($data['custom_unit_input']) ? $data['custom_unit_input'] : "",
            'compare_with'           => $data['compare_with'],
            'target_value_input'     => isset($data['target_value_input']) ? $data['target_value_input'] : "",
            'drill_down'             => isset($data['drill_down']) ? true : false,
            'drill_down_columns'     => isset($data['drill_down_columns']) ? $data['drill_down_columns'] : ""
        ];

        if (!is_null(session()->get('metric_session_wizard_configuration'))) {
            $sessionData = $sessionData + session()->get('metric_session_wizard_configuration');
        }

        session()->put('metric_session_wizard_configuration', $sessionData);
        $this->metricRepository->update($metric);
    }

    public function isColumnDate($table, $column, $isSession = true, $metric = null)
    {

        if ($isSession) {
            $connection = session()->get('metric_session_wizard_configuration')["connection"];
        } else {
            $connection = $metric->getConnectionAsString();
        }

        return in_array($this->dataConnectionTablesService->getColumnType($connection, $table, $column), ["timestamp", "date", "datetime"]);

    }

    public function changeMetricCategory($metric, $data)
    {

        $this->checkMetricCreatorPermission($metric);

        $metric->category_id = $data['metric_category'];
        $metric->save();
    }

    private function checkMetricCreatorPermission(Resource $report)
    {

        if (
            !(auth()->user()->isAdminOrOwner() ||
                $report->resource_creator === auth()->user()->user_ID)
        ) {
            abort(403, trans('metrics.messages.unauthorized'));
        }

    }

    private function checkMetricAccessPermission(Resource $metric)
    {

        if (
            !((auth()->user() && auth()->user()->isAdminOrOwner()) || strtolower($metric->access_control_type) == "public" ||
                (auth()->user() && in_array(auth()->user()->group_id, $metric->resource_permissions->pluck("group_ID")->toArray())))
        ) {
            abort(403, trans('metrics.messages.unauthorized'));
        }

    }

    private function checkChartDrillAccessPermission(Resource $metric)
    {

        if (
            !(auth()->user()->isAdminOrOwner() || strtolower($metric->access_control_type) == "public" ||
                in_array(auth()->user()->group_id, $metric->resource_permissions->pluck("group_ID")->toArray()))
        ) {
            abort(403, trans('metrics.messages.unauthorized'));
        }

    }

    public function checkThereIsGroupsAndCategories()
    {

        if ($this->categoryService->getCategoriesCount() == 0 || $this->groupService->getGroupsCount() == 0) {
            abort(400, trans('metrics.messages.create_group_and_category'));
        }

    }

    private function getMetrics($request)
    {

        if (auth()->user()->isAdminOrOwner()) {
            return $this->getAllMetrics($request);
        } else {
            return $this->getAuthedUserMetrics($request);
        }

    }

}
