<?php

namespace App\Http\Controllers\Roles;

use App\Models\Role;
use App\Models\User;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Yajra\DataTables\DataTables;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Spatie\Permission\Models\Permission;
use App\Http\Requests\Roles\RoleStoreRequest;
use App\Services\DataTableActionLinksService;
use App\Http\Requests\Roles\RoleUpdateRequest;
use Illuminate\Routing\Controllers\Middleware;
use Illuminate\Routing\Controllers\HasMiddleware;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;

class RoleController extends Controller implements HasMiddleware
{
    private $notFoundMessage = 'Role not found';

    public static function middleware(): array
    {
        return [
            new Middleware('permission:role.view', only: ['index', 'dataTable']),
            new Middleware('permission:role.add', only: ['create', 'store', 'getPermissionsGroups']),
            new Middleware('permission:role.update', only: ['edit', 'update', 'getPermissionsGroups']),
            new Middleware('permission:role.delete', only: ['destroy', 'generateRoleErrorMessage', 'archivedDataTable']),
        ];
    }

    /**
     * Display a listing of the resource.
     */
    public function index(): View
    {
        return view('roles.index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create(): View
    {
        $permissionGroups = $this->getPermissionsGroups();

        return view('roles.create', compact('permissionGroups'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(RoleStoreRequest $request): RedirectResponse
    {
        try {

            DB::beginTransaction();

            $role = Role::create([
                'name' => $request->name,
                'updated_at' => null,
            ]);

            $role->givePermissionTo($request->input('permissions'));

            DB::commit();

            return redirect()->route('roles.index')->with('success', 'Role created successfully');
        } catch (\Exception $e) {
            DB::rollBack();
            if (app()->isLocal()) throw $e; // Let Laravel show the exception page
            return redirect()->back()->with('error', $e->getMessage());
        }
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(string $id): View
    {
        $role = Role::whereUuid($id)->where('name', '<>', 'super_admin')->with(['permissions'])->first();

        abort_if(empty($role), 404);

        $permissionGroups = $this->getPermissionsGroups();

        return view('roles.edit', compact('role', 'permissionGroups'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(RoleUpdateRequest $request, string $id): RedirectResponse
    {
        try {
            $role = Role::whereUuid($id)->where('name', '<>', 'super_admin')->withCount(['permissions'])->first();

            if (empty($role)) return redirect()->back()->with('error', $this->notFoundMessage);

            DB::beginTransaction();

            if ($role->is_deletable) {
                $role->update([
                    'name' => $request->input('name'),
                ]);
            }

            $role->syncPermissions($request->input('permissions'));

            DB::commit();

            return redirect()->route('roles.index')->with('success', 'Role updated successfully');
        } catch (\Exception $e) {
            DB::rollBack();
            if (app()->isLocal()) throw $e; // Let Laravel show the exception page
            return redirect()->back()->with('error', $e->getMessage());
        }
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id): JsonResponse
    {
        try {
            $role = Role::whereUuid($id)->where('name', '<>', 'super_admin')->withCount(['permissions', 'users'])->first();

            if (empty($role)) throw new NotFoundHttpException($this->notFoundMessage);

            if (!$role->is_deletable) throw new BadRequestException('The role is not deletable');

            if ($role->permissions_count > 0 || $role->users_count > 0) {
                throw new NotFoundHttpException($this->generateRoleErrorMessage($role));
            }

            DB::beginTransaction();

            $role->delete();

            DB::commit();

            return $this->jsonResponse(['message' => 'Role removed successfully.']);
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->jsonResponse($e->getMessage(), $e->getCode());
        }
    }

    private function getPermissionsGroups(): array
    {
        $permissions = Permission::get();
        $permissionGroupsArray = [];

        foreach ($permissions as $permission) {
            $permissionGroupsArray[$permission->group][] = $permission;
        }

        return $permissionGroupsArray;
    }

    private function generateRoleErrorMessage($role): string
    {
        if ($role->permissions_count > 0) {
            return trans('roles.toast.errors.permissions');
        }

        if ($role->users_count > 0) {
            return trans('roles.toast.errors.users');
        }

        return trans('global.toast.unable_to_remove');
    }

    public function dataTable(Request $request): JsonResponse
    {
        $roles = Role::withCount(['users', 'permissions']);
        $totalPermissionsCount = Permission::count();

        $dt = DataTables::of($roles);

        $dt->filter(function ($query) use ($request) {
            if ($request->has('search')) {
                $search = "%{$request->input('search')['value']}%";

                $query->where('name', 'like', $search);
            }
        });

        $dt->addColumn('name', function ($row) {
            if ($row->is_deletable)
                return ucwords(str_replace('_', ' ', $row->name));

            return ucwords(str_replace('_', ' ', $row->name)) . ' <span class="badge bg-outline-primary">' . trans('roles.home.text.system_roles') . '</span>';
        });

        $dt->addColumn('total_count', function ($row) {
            if ($row->users_count > 0) {
                return '<span class="badge badge-dim bg-outline-success">' . $row->users_count . '</span>';
            }
            return '<span class="badge badge-dim bg-outline-danger">' . $row->users_count . '</span>';
        });

        $dt->addColumn('total_permissions', function ($row) use ($totalPermissionsCount) {
            if ($row->name == Role::SUPER_ADMIN) {
                return $totalPermissionsCount . '/' . $totalPermissionsCount;
            }
            return $row->permissions_count . '/' . $totalPermissionsCount;
        });

        $dt->addColumn('created', function ($record) {
            return $record->createdAt();
        });

        $dt->addColumn('updated', function ($record) {
            return $record->updatedAt();
        });

        $dt->addColumn('actions', function ($record) {
            $links = [
                ['action' => 'update', 'syncResponse' => true],
                ['action' => 'delete', 'shouldRender' => $record->is_deletable],
            ];

            return (new DataTableActionLinksService(
                model: $record,
                routeNamespace: 'roles',
                permissionNamespace: 'role',
                datatableId: '#roles-dt',
                isLocked: $record->name == Role::SUPER_ADMIN
            ))->byArray($links);
        });

        $dt->addIndexColumn();
        $dt->rawColumns(['actions', 'name', 'total_count', 'created', 'updated']);

        return $dt->make(true);
    }

    /**
     * Return the listing of the resource.
     */
    public function archivedDataTable(Request $request): JsonResponse
    {
        $roles = Role::withCount(['users', 'permissions'])->onlyTrashed();
        $totalPermissionsCount = Permission::count();

        $dt = DataTables::of($roles);

        $dt->filter(function ($query) use ($request) {
            if ($request->has('search')) {
                $search = "%{$request->input('search')['value']}%";

                $query->where('name', 'like', $search);
            }
        });

        $dt->addColumn('name', function ($row) {
            if ($row->is_deletable)
                return ucwords(str_replace('_', ' ', $row->name));

            return ucwords(str_replace('_', ' ', $row->name)) . ' <span class="badge badge-outline-primary">' . trans('roles.home.page.system_role') . '</span>';
        });

        $dt->addColumn('total_count', function ($row) {
            if ($row->users_count > 0) {
                return '<span class="badge badge-dim bg-outline-success">' . $row->users_count . '</span>';
            }
            return '<span class="badge badge-dim bg-outline-danger">' . $row->users_count . '</span>';
        });

        $dt->addColumn('total_permissions', function ($row) use ($totalPermissionsCount) {
            if ($row->name == 'super_admin') {
                return $totalPermissionsCount . '/' . $totalPermissionsCount;
            }
            return $row->permissions_count . '/' . $totalPermissionsCount;
        });

        $dt->addColumn('created', function ($record) {
            return humanTime($record->created_at);
        });

        $dt->addColumn('updated', function ($record) {
            return empty($record->updated_at) ? '<small><i>' . trans('global.text.never_updated') . '</i></small>' : humanTime($record->updated_at);
        });

        $dt->addColumn('actions', function ($record) {
            $links = [
                ['action' => 'restore'],
            ];

            return (new DataTableActionLinksService(
                model: $record,
                routeNamespace: 'roles',
                permissionNamespace: 'role',
                datatableId: '#archived-roles-dt',
                isLocked: $record->name == Role::SUPER_ADMIN
            ))->byArray($links);
        });

        $dt->addIndexColumn();
        $dt->rawColumns(['actions', 'name', 'total_count', 'created', 'updated']);

        return $dt->make(true);
    }
}
