<?php

namespace App\Http\Controllers\Permission;

use App\Http\Controllers\BaseController;
use App\Models\User;
use App\Models\Group;
use App\Models\GroupPermission;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use DB;

class AdminController extends BaseController {
    public function get_group(Request $_r, $_group_id) {
        // We love disgusting single queries for speed...
        $group = Group::select('name', DB::raw('string_agg(concat(concat_ws(\':\', permission, conditional), \'=\', status), \' \') as scopes'))
            ->where('groups.id', '=', $_group_id)
            ->leftJoin('group_permissions', 'groups.id', '=', 'group_id')
            ->groupBy('groups.id')
            ->get()
            ->first()
            ->toArray();

        $group['scopes'] = preg_match_all('/^(\s?[^\s]+(?:\w(?:\:\w+)*\=\d))+$/', $group['scopes']) ? explode(' ', $group['scopes']) : '';

        if(!$group) {
            return response()->json(['detail' => 'Error processing the request'], 500);
        }

        return response()->json($group, 200);
    }

    public function get_groups(Request $_r) {
        // Build our query function
        $query_builder = function() {
            return Group::select('groups.id', 'name', DB::raw('string_agg(concat_ws(\'=\', concat_ws(\':\', permission, conditional), status), \' \') as scopes'))
                ->leftJoin('group_permissions', 'groups.id', '=', 'group_id')
                ->groupBy('groups.id');            
        };

        // Build our search function
        $query_search = function($prefix_query, $global, $filters) {

            if($global) {
                $prefix_query = $prefix_query->having(DB::raw("string_agg(concat_ws('=', concat_ws(':', permission, conditional), status), ' ')"), 'LIKE', "%$global%")
                    ->orHaving('name', 'LIKE', "%$global%");
            }

            if($filters) {
                foreach($filters as $filter) {
                    $col = '';
                    switch($filter['id']) {
                        case 'name':
                            $prefix_query = $prefix_query->having('name', 'LIKE', "%$global%");
                            break;
                        case 'scopes':
                            $prefix_query = $prefix_query->having(DB::raw("string_agg(concat_ws('=', concat_ws(':', permission, conditional), status), ' ')"), 'LIKE', "%$global%");
                            break;
                    }
                }
            }

            return $prefix_query;
        };

        // Build our sorting function
        $query_sort = function($prefix_query, $column, $direction) {
            switch($column) {
                case 'name':
                    $prefix_query = $prefix_query->orderBy('name', $direction);
                    break;
                case 'scopes':
                    $prefix_query = $prefix_query->orderBy('scopes', $direction);
                    break;
                default:
                    $prefix_query = $prefix_query->orderBy('name', $direction ?? 'asc');
            }

            return $prefix_query;
        };

        // Create valid sorting keys
        $sorting_keys = ['updated_at', 'name', 'scopes'];

        // Build our transformation function
        $query_transform = function($data) {
            $data->transform(function($row) {
                return $row;
            });

            return $data;
        };

        return BaseController::pagination_table_request($_r, $query_builder, $query_search, $query_sort, $query_transform, $sorting_keys);
    }

    public function put_group(Request $_r, $_group_id = 0) {
        try {
            $valid_request = !Validator::make([
                'name'      => $_r->input('name'),
                'scopes'    => $_r->input('scopes'),
            ], [
                'name'      => ['required', 'max:255'],
                'scopes.*'  => ['regex:/[^\s]+(?:\w(?:\:\w+)*\=\d)/'],
            ])->fails();

            if($valid_request) {
                // Wrap in a transaction because we want everything to rollback if anything fails.
                $successful_creation = DB::transaction(function () use($_r, $_group_id) {
                    // Find or create group
                    $group = $_group_id ? Group::find($_group_id) : new Group;

                    $group->fill([
                        'name' => $_r->input('name'),
                    ]);
    
                    $scopes_to_insert = [];
        
                    // If the group is saved we can do the rest, otherwise we didn't successfully create or update anything.
                    if($group->save()) {
                        // Delete group permissions that already exist.
                        GroupPermission::where('group_id', '=', $group->id)->delete();

                        foreach($_r->input('scopes') as $scope) {
                            [$proposition, $value] = explode('=', $scope);

                            // Split the proposition into a name and conditional if possible.
                            $proposition = explode(':', $proposition);

                            $name = $proposition[0];
                            $conditional = count($proposition) === 1 ? null : $proposition[1];
        
                            $scopes_to_insert[] = [
                                'group_id' => $group->id,
                                'permission' => $name,
                                'conditional' => $conditional,
                                'status' => $value
                            ];
                        }
    
                        // Insert scopes into group permissions.
                        DB::table('group_permissions')->insert($scopes_to_insert);

                        return true;
                    }
                    
                    return false;
                });

                if($successful_creation) {
                    return response()->json(['detail' => 'Group edited or created'], 200);
                }
            }
        }
        catch(Exception $ex) {}

        return response()->json(['detail' => 'Error making or editing group'], 500);
    }

    public function delete_user(Request $_r, $_group_id) {
        try {
            $valid_request = !Validator::make([ 'id' => $_group_id ], [
                'id' => ['required', 'exists:groups,id'],
                'reassign' => ['required', 'exists:group,id']
            ])->fails();

            if($valid_request) {
                // Wrap in a transaction because we want everything to rollback if anything fails.
                DB::transaction(function () {
                    // Reassign users to new group
                    User::where('group_id', '=', $_group_id)->update(['group_id' => $_group_id]);
                    
                    // Delete scopes
                    GroupPermission::where('group_id', '=', $_group_id)->delete();
                    
                    // Delete group
                    Group::find($_group_id)->delete();
                });
                
                return response()->json(['detail' => 'Group deleted'], 200);
            }
        }
        catch(Exception $ex) {}

        return response()->json(['detail' => 'Error deleting user'], 500);
    }
}
