<?php

namespace App\Http\Controllers\Permission;

// Laravel Specifics
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use DB;

// Creatia Specifics
use App\Http\Controllers\BaseController;
use App\Models\User;
use App\Models\Group;
use App\Models\GroupPermission;
use App\Rules\GroupIdentity;

class AdminController extends BaseController {
    public function get_group(Request $_r, $_group_id) {
        // We love disgusting single queries for speed...
        $group = Group::select(
                'groups.name',
                DB::raw("string_agg(permission || CASE WHEN conditional IS NOT NULL AND conditional <> '' THEN ':' || conditional ELSE '' END || CASE WHEN identifier IS NOT NULL AND identifier <> '' THEN '?' || identifier ELSE '' END || CASE WHEN status IS NOT NULL AND status::text <> '' THEN '=' || status::text ELSE '' END, ' ') as scopes")
            )
            ->where('groups.id', '=', $_group_id)
            ->leftJoin('group_permissions', 'groups.id', '=', 'group_id')
            ->groupBy('groups.id')
            ->first()
            ->toArray();

        $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) {
        return $this->paged_table($_r, function() {
            return Group::select(
                    'groups.id',
                    'name',
                    DB::raw("string_agg(permission || CASE WHEN conditional IS NOT NULL AND conditional <> '' THEN ':' || conditional ELSE '' END || CASE WHEN identifier IS NOT NULL AND identifier <> '' THEN '?' || identifier ELSE '' END || CASE WHEN status IS NOT NULL AND status::text <> '' THEN '=' || status::text ELSE '' END, ' ') as scopes")    
                )
                ->leftJoin('group_permissions', 'groups.id', '=', 'group_id')
                ->groupBy('groups.id');
        }, [ 
            'name'      => [ 'db' => 'name', 'searchable' => true ],
            'scopes'    => [ 'db' => 'scopes', 'searchable' => false ]
        ], 'name', null);
    }

    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) {

                            // Split the string up into correspondent parts
                            $pattern = '/^(?<permission>[^:?=]+)(?::(?<right>[^?=]+))?(?:\?(?<identifier>[^=]+))?(?:=(?<value>.+))?$/';

                            preg_match($pattern, $scope, $matches);

                            $scopes_to_insert[] = [
                                'group_id'          => $group->id,
                                'permission'        => $matches['permission'],
                                'conditional'       => $matches['right'] ?? null,
                                'identifier'        => $matches['identifier'] ?? null,
                                'status'            => $matches['value'] ?? 1
                            ];
                        }
    
                        // 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(\Throwable $e) {}

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

    // TO DO - Should only allow if there's nobody in the group...

    public function delete_group(Request $_r, $_group_id) {
        try {
            $valid_request = !Validator::make([ 'id' => $_group_id, 'reassign' => $_r->input('reassign') ], [
                'id' => ['required', new GroupIdentity],
                'reassign' => ['required', 'different:id', new GroupIdentity]
            ])->fails();

            if($valid_request) {

                $reassign = $_r->input('reassign');

                // Wrap in a transaction because we want everything to rollback if anything fails.
                DB::transaction(function () use ($_group_id, $reassign) {
                    // Reassign users to new group
                    User::where('group_id', '=', $_group_id)->update(['group_id' => $reassign]);

                    // Delete scopes
                    GroupPermission::where('group_id', '=', $_group_id)->delete();

                    // Delete group
                    Group::find($_group_id)->delete();
                });

                return response()->json(['detail' => 'Group deleted'], 200);
            }
        }
        catch(\Throwable $e) {}

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