<?php

namespace App\Actions\Monitoring;

use App\Models\Server;
use Carbon\Carbon;
use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;

class GetMetrics
{
    public function filter(Server $server, array $input): Collection
    {
        if (isset($input['from']) && isset($input['to']) && $input['from'] === $input['to']) {
            $input['from'] = Carbon::parse($input['from'])->format('Y-m-d').' 00:00:00';
            $input['to'] = Carbon::parse($input['to'])->format('Y-m-d').' 23:59:59';
        }

        $defaultInput = [
            'period' => '10m',
        ];

        $input = array_merge($defaultInput, $input);

        return $this->metrics(
            server: $server,
            fromDate: $this->getFromDate($input),
            toDate: $this->getToDate($input),
            interval: $this->getInterval($input)
        );
    }

    private function metrics(
        Server $server,
        Carbon $fromDate,
        Carbon $toDate,
        ?Expression $interval = null
    ): Collection {
        return DB::table('metrics')
            ->where('server_id', $server->id)
            ->whereBetween('created_at', [$fromDate->format('Y-m-d H:i:s'), $toDate->format('Y-m-d H:i:s')])
            ->select(
                [
                    DB::raw('created_at as date'),
                    DB::raw('ROUND(AVG(load), 2) as load'),
                    DB::raw('ROUND(AVG(memory_total), 2) as memory_total'),
                    DB::raw('ROUND(AVG(memory_used), 2) as memory_used'),
                    DB::raw('ROUND(AVG(memory_free), 2) as memory_free'),
                    DB::raw('ROUND(AVG(disk_total), 2) as disk_total'),
                    DB::raw('ROUND(AVG(disk_used), 2) as disk_used'),
                    DB::raw('ROUND(AVG(disk_free), 2) as disk_free'),
                    $interval,
                ],
            )
            ->groupByRaw('date_interval')
            ->orderBy('date_interval')
            ->get()
            ->map(function ($item) {
                $item->date = Carbon::parse($item->date)->format('Y-m-d H:i');

                return $item;
            });
    }

    private function getFromDate(array $input): Carbon
    {
        if ($input['period'] === 'custom') {
            return new Carbon($input['from']);
        }

        return Carbon::parse('-'.convert_time_format($input['period']));
    }

    private function getToDate(array $input): Carbon
    {
        if ($input['period'] === 'custom') {
            return new Carbon($input['to']);
        }

        return Carbon::now();
    }

    private function getInterval(array $input): Expression
    {
        if ($input['period'] === 'custom') {
            $from = new Carbon($input['from']);
            $to = new Carbon($input['to']);
            $periodInHours = $from->diffInHours($to);
        }

        if (! isset($periodInHours)) {
            $periodInHours = Carbon::parse(
                convert_time_format($input['period'])
            )->diffInHours();
        }

        if (abs($periodInHours) <= 1) {
            return DB::raw("strftime('%Y-%m-%d %H:%M:00', created_at) as date_interval");
        }

        if ($periodInHours <= 24) {
            return DB::raw("strftime('%Y-%m-%d %H:00:00', created_at) as date_interval");
        }

        return DB::raw("strftime('%Y-%m-%d 00:00:00', created_at) as date_interval");
    }

    public static function rules(array $input): array
    {
        $rules = [
            'period' => [
                'required',
                Rule::in([
                    '10m',
                    '30m',
                    '1h',
                    '12h',
                    '1d',
                    '7d',
                    'custom',
                ]),
            ],
        ];

        if (isset($input['period']) && $input['period'] === 'custom') {
            $rules['from'] = ['required', 'date', 'before:to'];
            $rules['to'] = ['required', 'date', 'after:from'];
        }

        return $rules;
    }
}
