<?php

namespace App\Http\Livewire;

use App\Models\Activation;
use App\Models\DashboardWidget;
use App\Models\DownloadLog;
use App\Models\LicenseKey;
use App\Models\Setting;
use Illuminate\Support\Number;
use Illuminate\Validation\Rule;
use Livewire\Component;
use App\Models\Telemetry;

class Dashboard extends Component {

    public $customWidgets, $telemetryDataCount, $customWidgetData, $widgetsItemsOrder, $columns, $conjunction, $widgetWidth, $visibility, $visibilityTemp, $deleteTemp, $positions, $widgetName, $chartType, $telemetryGroupName, $xAxis, $yAxis, $isCorrectFormat, $licenseKeyExists, $productExists, $productVersionExists, $daysPerGraph, $activationsGraphData, $licenseKeysGraphData, $telemetryGraphData, $downloadsGraphData, $totalActivations, $todayActivations, $totalLicenseKeys, $todayLicenseKeys, $totalDownloads, $todayDownloads, $totalTelemetry, $todayTelemetry, $productId;

    protected $listeners = [ 'addWidget', 'saveWidgetsConfiguration', 'resetInputs' ];

    public function mount(): void {
        $columnsRawValues = Setting::select( [ 'name', 'value' ] )->where( 'group', 'widgets-columns' )->orderBy( 'value' )->get()->pluck( 'value', 'name' )->toArray();
        $columns          = [
            'built-in-telemetry-data' => '5',
            'built-in-activations'    => '7',
            'built-in-downloads'      => '8',
            'built-in-license-keys'   => '4',
        ];

        foreach ( $columns as $key => $value ) {
            if ( ! isset( $columnsRawValues[ $key ] ) ) {
                $columnsRawValues[ $key ] = $value;
            }
        }

        $this->columns = $columnsRawValues;

        $visibilityRawValues = Setting::select( [ 'name', 'value' ] )->where( 'group', 'widgets-visibility' )->orderBy( 'value' )->get()->pluck( 'value', 'name' )->toArray();
        $visibility          = [
            'built-in-telemetry-data' => false,
            'built-in-activations'    => false,
            'built-in-downloads'      => false,
            'built-in-license-keys'   => false,
        ];

        foreach ( $visibility as $key => $value ) {
            if ( ! isset( $visibilityRawValues[ $key ] ) ) {
                $visibilityRawValues[ $key ] = $value;
            } else {
                $visibilityRawValues[ $key ] = (bool) $visibilityRawValues[ $key ];
            }
        }

        $this->visibility     = $visibilityRawValues;
        $this->visibilityTemp = $visibilityRawValues;

        $positionsRawValues = Setting::select( [ 'name', 'value' ] )->where( 'group', 'widgets-positions' )->orderBy( 'value' )->get()->pluck( 'value', 'name' )->toArray();
        $positions          = [
            'built-in-telemetry-data' => 1,
            'built-in-activations'    => 2,
            'built-in-downloads'      => 3,
            'built-in-license-keys'   => 4,
        ];

        foreach ( $positions as $key => $value ) {
            if ( ! isset( $positionsRawValues[ $key ] ) ) {
                $positionsRawValues[ $key ] = $value;
            }
        }

        uasort( $positionsRawValues, function ( $a, $b ) {
            return $a <=> $b;
        } );

        $customWidgets = DashboardWidget::all()->toArray();
        foreach ( $customWidgets as $widget ) {
            $this->customWidgets[ 'custom-' . $widget['id'] ] = $widget;
            $this->deleteTemp[ 'custom-' . $widget['id'] ]    = false;
        }

        $this->positions = $positionsRawValues;
    }

    public function daysPerGraphChanged(): void {
        $this->calculateValues();

        $this->dispatchBrowserEvent( 'redrawGraph', array_merge( $this->customWidgetData, [
            'telemetryGraphData'   => $this->telemetryGraphData,
            'activationsGraphData' => $this->activationsGraphData,
            'downloadsGraphData'   => $this->downloadsGraphData,
            'licenseKeysGraphData' => $this->licenseKeysGraphData
        ] ) );
    }

    public function addWidgetRules(): array {
        return [
            'chartType'            => [ 'required', Rule::in( [ 'line', 'bar', 'pie' ] ) ],
            'widgetName'           => [ 'required', 'string' ],
            'telemetryGroupName'   => [ 'required', 'string' ],
            'xAxis'                => [ 'required', Rule::in( [ 'days', 'records-count', 'numeric-data-sum' ] ) ],
            'yAxis'                => [ 'required', Rule::in( [ 'records-count', 'numeric-data-sum' ] ) ],
            'conjunction'          => [ 'required', Rule::in( [ 'AND', 'OR' ] ) ],
            'isCorrectFormat'      => [ 'required', 'boolean' ],
            'licenseKeyExists'     => [ 'required', 'boolean' ],
            'productExists'        => [ 'required', 'boolean' ],
            'productVersionExists' => [ 'required', 'boolean' ],
            'productId'            => [ 'required', 'numeric', 'min:0' ],
            'widgetWidth'          => [ 'required', 'numeric', 'min:4' ],
        ];
    }

    public function addWidgetItemUpdated( $propertyName ): void {
        $this->validateOnly( $propertyName, $this->addWidgetRules() );
    }

    public function addWidget(): void {
        $this->validate( $this->addWidgetRules() );

        $widget                         = new DashboardWidget();
        $widget->name                   = $this->widgetName;
        $widget->product_id             = $this->productId;
        $widget->chart_type             = $this->chartType;
        $widget->data_group             = $this->telemetryGroupName;
        $widget->x_axis                 = $this->xAxis;
        $widget->y_axis                 = $this->yAxis;
        $widget->is_correct_format      = $this->isCorrectFormat;
        $widget->license_key_exists     = $this->licenseKeyExists;
        $widget->product_exists         = $this->productExists;
        $widget->product_version_exists = $this->productVersionExists;
        $widget->conjunction_type       = $this->conjunction;

        $widget->save();

        Setting::upsert( [ 'group' => 'widgets-positions', 'name' => 'custom-' . $widget->id, 'value' => 1 ], [ 'group', 'name' ], [ 'value' ] );
        Setting::upsert( [ 'group' => 'widgets-columns', 'name' => 'custom-' . $widget->id, 'value' => $this->widgetWidth ], [ 'group', 'name' ], [ 'value' ] );

        $this->dispatchBrowserEvent( 'close-create-modal' );
    }

    public function saveWidgetsConfiguration(): void {
        if ( $this->widgetsItemsOrder != '' ) {
            $positions = explode( ',', $this->widgetsItemsOrder );

            foreach ( $positions as $index => $position ) {
                if ( intval( $position ) > 0 ) {
                    Setting::upsert( [ 'group' => 'widgets-positions', 'name' => 'custom-' . $position, 'value' => $index ], [ 'group', 'name' ], [ 'value' ] );
                } else {
                    Setting::upsert( [ 'group' => 'widgets-positions', 'name' => $position, 'value' => $index ], [ 'group', 'name' ], [ 'value' ] );
                }
            }
        }

        foreach ( $this->columns as $index => $width ) {
            Setting::upsert( [ 'group' => 'widgets-columns', 'name' => $index, 'value' => $width ], [ 'group', 'name' ], [ 'value' ] );
        }

        foreach ( $this->visibilityTemp as $index => $width ) {
            Setting::upsert( [ 'group' => 'widgets-visibility', 'name' => $index, 'value' => (bool) $width ], [ 'group', 'name' ], [ 'value' ] );
        }

        if ( $this->deleteTemp ) {
            foreach ( $this->deleteTemp as $key => $value ) {
                if ( $value ) {
                    Setting::where( 'name', $key )->delete();
                    DashboardWidget::find( str_replace( 'custom-', '', $key ) )->delete();
                }
            }
        }


        $this->dispatchBrowserEvent( 'close-reorder-modal' );
    }

    public function render() {
        $this->calculateValues();

        return view( 'livewire.dashboard.dashboard' );
    }

    public function resetInputs(): void {
        $this->chartType            = 'line';
        $this->xAxis                = 'days';
        $this->yAxis                = 'records-count';
        $this->isCorrectFormat      = true;
        $this->licenseKeyExists     = true;
        $this->productExists        = true;
        $this->productVersionExists = true;
        $this->productId            = 0;
        $this->widgetWidth          = 4;
        $this->conjunction          = 'AND';
    }

    public function calculateValues(): void {
        $this->daysPerGraph = ( (int) $this->daysPerGraph > 0 && in_array( $this->daysPerGraph, [ 7, 15, 30 ] ) ) ? $this->daysPerGraph : getOption( 'ui', 'daysPerGraph', 7 );

        $this->totalActivations = Number::abbreviate( Activation::sum( 'activation_counter' ) );
        $this->todayActivations = Activation::whereBetween( 'created_at', [ now()->startOfDay(), now()->endOfDay() ] )->sum( 'activation_counter' );

        $this->totalLicenseKeys = Number::abbreviate( LicenseKey::count() );
        $this->todayLicenseKeys = LicenseKey::whereBetween( 'created_at', [ now()->startOfDay(), now()->endOfDay() ] )->count();

        $this->totalDownloads = Number::abbreviate( DownloadLog::count() );
        $this->todayDownloads = DownloadLog::whereBetween( 'created_at', [ now()->startOfDay(), now()->endOfDay() ] )->count();

        $this->totalTelemetry = Number::abbreviate( Telemetry::count() );
        $this->todayTelemetry = Telemetry::whereBetween( 'created_at', [ now()->startOfDay(), now()->endOfDay() ] )->count();

        $days = [];
        for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
            $days[] = now()->subDays( $i )->format( 'M j' );
        }

        if ( $this->visibility['built-in-activations'] ) {
            $this->activationsGraphData         = [];
            $this->activationsGraphData['days'] = $days;
            for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
                $this->activationsGraphData['activations'][] = Activation::whereBetween( 'created_at', [
                        now()->subDays( $i )->startOfDay(),
                        now()->subDays( $i )->endOfDay()
                    ]
                )->sum( 'activation_counter' );
            }
        }

        if ( $this->visibility['built-in-telemetry-data'] ) {
            $this->telemetryGraphData         = [];
            $this->telemetryGraphData['days'] = $days;
            for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
                $this->telemetryGraphData['correct'][]   = Telemetry::where( 'has_red_flags', false )->whereBetween( 'created_at', [ now()->subDays( $i )->startOfDay(), now()->subDays( $i )->endOfDay() ] )->count();
                $this->telemetryGraphData['incorrect'][] = Telemetry::where( 'has_red_flags', true )->whereBetween( 'created_at', [ now()->subDays( $i )->startOfDay(), now()->subDays( $i )->endOfDay() ] )->count();
            }
        }

        if ( $this->visibility['built-in-downloads'] ) {
            $this->downloadsGraphData         = [];
            $this->downloadsGraphData['days'] = $days;
            for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
                $this->downloadsGraphData['downloads'][] = DownloadLog::whereBetween( 'created_at', [
                    now()->subDays( $i )->startOfDay(),
                    now()->subDays( $i )->endOfDay()
                ] )->count();
            }
        }

        if ( $this->visibility['built-in-license-keys'] ) {
            $this->licenseKeysGraphData = [];
            $statuses                   = LicenseKey::getStatuses();
            foreach ( $statuses as $key => $status ) {
                $this->licenseKeysGraphData['status'][] = $status['text'];
                $this->licenseKeysGraphData['count'][]  = LicenseKey::where( 'status', $key )->where( 'created_at', '>=', now()->subDays( $this->daysPerGraph )->startOfDay() )->count();
                $this->licenseKeysGraphData['color'][]  = $status['color'];
            }
        }

        $this->customWidgetData = [];

        if ( $this->customWidgets ) {
            foreach ( $this->customWidgets as $widget ) {
                $x = $days;
                $y = [];

                $flagsWhere = [
                    'is_correct_format'      => $widget['is_correct_format'],
                    'license_key_exists'     => $widget['license_key_exists'],
                    'product_exists'         => $widget['product_exists'],
                    'product_version_exists' => $widget['product_version_exists'],

                ];

                $numericSingleValueWhere = [
                    'data_group' => $widget['data_group'],
                    'data_type'  => 'numeric-single-value'
                ];

                $flags = function ( $q ) use ( $widget, $flagsWhere ) {
                    if ( $widget['conjunction_type'] == 'AND' ) {
                        foreach ( $flagsWhere as $key => $value ) {
                            $q->where( $key, $value );
                        }
                    } elseif ( $widget['conjunction_type'] == 'OR' ) {
                        foreach ( $flagsWhere as $key => $value ) {
                            $q->orWhere( $key, $value );
                        }
                    }
                };

                if ( $widget['y_axis'] == 'numeric-data-sum' ) {
                    for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
                        $y[] = Telemetry::where( $flags )->where( $numericSingleValueWhere )->whereBetween( 'created_at', [ now()->subDays( $i )->startOfDay(), now()->subDays( $i )->endOfDay() ] )->sum( 'data' );
                    }
                } else if ( $widget['y_axis'] == 'records-count' ) {
                    for ( $i = (int) $this->daysPerGraph; $i >= 0; $i -- ) {
                        $y[] = Telemetry::where( $flags )->where( $numericSingleValueWhere )->whereBetween( 'created_at', [ now()->subDays( $i )->startOfDay(), now()->subDays( $i )->endOfDay() ] )->count();
                    }
                }

                $this->customWidgetData[ 'customWidget' . $widget['id'] ] = [
                    'x' => $x,
                    'y' => $y
                ];
            }
        }

        $this->telemetryDataCount = [
            'incorrectFormat'         => Number::abbreviate( Telemetry::where( 'is_correct_format', false )->whereBetween( 'created_at', [ now()->subDays( (int) $this->daysPerGraph )->startOfDay(), now()->endOfDay() ] )->count() ),
            'invalidLicenseKeys'      => Number::abbreviate( Telemetry::where( 'license_key_exists', false )->whereBetween( 'created_at', [ now()->subDays( (int) $this->daysPerGraph )->startOfDay(), now()->endOfDay() ] )->count() ),
            'invalidProduct'          => Number::abbreviate( Telemetry::where( 'product_exists', false )->whereBetween( 'created_at', [ now()->subDays( (int) $this->daysPerGraph )->startOfDay(), now()->endOfDay() ] )->count() ),
            'invalidVersions'         => Number::abbreviate( Telemetry::where( 'product_version_exists', false )->whereBetween( 'created_at', [ now()->subDays( (int) $this->daysPerGraph )->startOfDay(), now()->endOfDay() ] )->count() ),
        ];
    }
}
