<?php

/**
 * ============================================================
 *
 * @project   biiproject
 *
 * @author    Andika Debi Putra
 *
 * @email     andikadebiputra@gmail.com
 *
 * @website   https://biiproject.com
 *
 * @copyright Copyright (c) 2026 Andika Debi Putra
 * @license   Proprietary - All Rights Reserved
 *
 * @version   1.0.0
 *
 * @created   2026-05-01
 * ============================================================
 *
 * Unauthorized copying, modification, distribution, or use
 * of this file is strictly prohibited without prior written
 * permission from the author.
 * ============================================================
 */

namespace App\Http\Controllers\AccessControl;

use App\Models\Permission;
use App\Support\DataTable;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;

class PermissionManagementController extends Controller
{
    public function __construct()
    {
        // Middleware handled in web.php
    }

    // show all permissions with relations
    public function index(Request $request)
    {
        if (DataTable::isDataTableRequest($request)) {
            return $this->dataTable($request);
        }

        // render view with data
        return view('pages.access_control.permissions');
    }

    protected function dataTable(Request $request)
    {
        try {
            $query = Permission::query()->with(['roles:id,name', 'creator:id,email', 'updater:id,email']);
            $recordsTotal = Permission::count();
            $globalSearch = DataTable::globalSearch($request);

            $authUser = $request->user();
            $canManage = $authUser->can('manage access rights');

            if ($canManage) {
                $status = DataTable::columnSearch($request, 0);
                $name = DataTable::columnSearch($request, 1);
                $guard = DataTable::columnSearch($request, 2);
                $role = DataTable::columnSearch($request, 3);
            } else {
                $status = null;
                $name = DataTable::columnSearch($request, 0);
                $guard = DataTable::columnSearch($request, 1);
                $role = DataTable::columnSearch($request, 2);
            }

            if ($status) {
                $query->where('is_active', $status === 'active');
            }

            if ($name) {
                $query->where('name', 'like', "%{$name}%");
            }

            if ($guard) {
                $query->where('guard_name', 'like', "%{$guard}%");
            }

            if ($role) {
                $query->whereHas('roles', function ($roleQuery) use ($role) {
                    $roleQuery->where('name', 'like', "%{$role}%");
                });
            }

            if ($canManage) {
                $createdAt = DataTable::columnSearch($request, 4);
                $createdBy = DataTable::columnSearch($request, 5);
                $updatedAt = DataTable::columnSearch($request, 6);
                $updatedBy = DataTable::columnSearch($request, 7);
            } else {
                $createdAt = DataTable::columnSearch($request, 3);
                $createdBy = DataTable::columnSearch($request, 4);
                $updatedAt = DataTable::columnSearch($request, 5);
                $updatedBy = DataTable::columnSearch($request, 6);
            }

            if ($createdAt) {
                $query->whereDate('created_at', $createdAt);
            }

            if ($createdBy) {
                $query->whereHas('creator', function ($creatorQuery) use ($createdBy) {
                    $creatorQuery->where('email', 'like', "%{$createdBy}%");
                });
            }

            if ($updatedAt) {
                $query->whereDate('updated_at', $updatedAt);
            }

            if ($updatedBy) {
                $query->whereHas('updater', function ($updaterQuery) use ($updatedBy) {
                    $updaterQuery->where('email', 'like', "%{$updatedBy}%");
                });
            }

            if ($globalSearch) {
                $query->where(function ($searchQuery) use ($globalSearch) {
                    $searchQuery
                        ->where('name', 'like', "%{$globalSearch}%")
                        ->orWhere('guard_name', 'like', "%{$globalSearch}%")
                        ->orWhereHas('roles', function ($roleQuery) use ($globalSearch) {
                            $roleQuery->where('name', 'like', "%{$globalSearch}%");
                        })
                        ->orWhereHas('creator', function ($creatorQuery) use ($globalSearch) {
                            $creatorQuery->where('email', 'like', "%{$globalSearch}%");
                        })
                        ->orWhereHas('updater', function ($updaterQuery) use ($globalSearch) {
                            $updaterQuery->where('email', 'like', "%{$globalSearch}%");
                        });
                });
            }

            // Perform filtered count WITHOUT eager loading or ordering
            $countQuery = clone $query;
            $countQuery->setEagerLoads([]);
            $countQuery->orders = null;
            $recordsFiltered = $countQuery->count();
            [$orderIndex, $orderDirection] = DataTable::order($request, $canManage ? 4 : 3, 'desc');

            $sortColumn = match (true) {
                $canManage && $orderIndex === 0 => 'is_active',
                ($canManage && $orderIndex === 1) || (! $canManage && $orderIndex === 0) => 'name',
                ($canManage && $orderIndex === 2) || (! $canManage && $orderIndex === 1) => 'guard_name',
                ($canManage && $orderIndex === 4) || (! $canManage && $orderIndex === 3) => 'created_at',
                ($canManage && $orderIndex === 6) || (! $canManage && $orderIndex === 5) => 'updated_at',
                default => 'created_at',
            };

            $permissions = $query
                ->orderBy($sortColumn, $orderDirection)
                ->skip(DataTable::start($request))
                ->take(DataTable::length($request))
                ->get();

            $rows = $permissions->map(function (Permission $permission) use ($canManage) {
                $row = [];

                if ($canManage) {
                    $row[] = sprintf(
                        '<div class="form-check form-switch d-flex align-items-center gap-2"><input class="form-check-input permission-toggle" type="checkbox" %s data-id="%d" data-name="%s"><span class="status-label %s">%s</span></div>',
                        $permission->is_active ? 'checked' : '',
                        $permission->id,
                        e($permission->name),
                        $permission->is_active ? 'text-success' : 'text-danger',
                        $permission->is_active ? 'Active' : 'Inactive'
                    );
                }

                $roleNames = $permission->roles->pluck('name');
                $roleBadges = $roleNames->isNotEmpty()
                    ? $roleNames->map(fn ($role) => '<span class="badge badge-slate-pill">'.e($role).'</span>')->implode(' ')
                    : '-';

                $row[] = e($permission->name);
                $row[] = e($permission->guard_name);
                $row[] = $roleBadges;
                $row[] = e(format_datetime($permission->created_at));
                $row[] = e($permission->creator->email ?? 'System');
                $row[] = e(format_datetime($permission->updated_at));
                $row[] = e($permission->updater->email ?? '-');

                if ($canManage) {
                    $row[] = '<div class="text-end">'
                        .'<button class="btn btn-link btn-edit" data-id="'.$permission->id.'" data-name="'.e($permission->name).'" data-guard="'.e($permission->guard_name).'" data-created="'.e(format_datetime($permission->created_at)).'" data-created-by="'.e($permission->creator->email ?? 'System').'" data-updated-at="'.e(format_datetime($permission->updated_at)).'" data-updated-by="'.e($permission->updater->email ?? '-').'" data-bs-toggle="modal" data-bs-target="#editPermissionModal"><i class="bi bi-pencil"></i></button>'
                        .'<button class="btn btn-link text-danger btn-delete" data-id="'.$permission->id.'" data-name="'.e($permission->name).'"><i class="bi bi-trash"></i></button>'
                        .'</div>';
                }

                return $row;
            })->all();

            return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
        } catch (\Exception $e) {
            Log::error('DataTable Error [PermissionManagement]: '.$e->getMessage());

            return DataTable::response($request, 0, 0, []);
        }
    }

    // create new permission
    public function store(Request $request)
    {
        // validate input fields
        $request->validate([
            'name' => [
                'required',
                'string',
                'min:3',
                'max:100',
                'regex:/^[a-zA-Z0-9_\-\.\/]+$/',
                Rule::unique('permissions', 'name')
                    ->where(fn ($query) => $query->where('guard_name', $request->guard_name)
                    ),
            ],
            'guard_name' => [
                'required',
                'string',
                'in:web,api',
            ],
        ], [
            'name.required' => __('Permission name is required.'),
            'name.regex' => __('Permission name contains invalid characters.'),
            'name.unique' => __('A permission with this Name & Guard combination already exists.'),
            'guard_name.required' => __('Guard is required.'),
            'guard_name.in' => __('Invalid guard selected.'),
        ]);

        try {
            // save permission record
            Permission::create([
                'name' => $request->name,
                'guard_name' => $request->guard_name,
                'created_by' => Auth::id(),
            ]);

            // success feedback
            if ($request->expectsJson()) {
                return response()->json([
                    'success' => true,
                    'message' => __('Permission has been successfully created.'),
                ]);
            }

            return redirect()->back()->with('success', __('Permission has been successfully created.'));

        } catch (\Exception $e) {
            if ($request->expectsJson()) {
                return response()->json([
                    'success' => false,
                    'message' => __('Failed to create permission: ').$e->getMessage(),
                ], 500);
            }

            return redirect()->back()->with('error', __('Failed to create permission: ').$e->getMessage());
        }
    }

    // update permission by id
    public function update(Request $request, $id)
    {

        // validate request input
        $request->validate([
            'name' => [
                'required',
                'string',
                'min:3',
                'max:100',
                'regex:/^[a-zA-Z0-9_\-\.\/]+$/',
                Rule::unique('permissions', 'name')
                    ->where(fn ($query) => $query->where('guard_name', $request->guard_name)
                    )
                    ->ignore($id),
            ],
            'guard_name' => [
                'required',
                'string',
                'in:web,api',
            ],
        ], [
            'name.required' => __('Permission name is required.'),
            'name.regex' => __('Permission name contains invalid characters.'),
            'name.unique' => __('A permission with this Name & Guard combination is already registered.'),
            'guard_name.required' => __('Guard is required.'),
            'guard_name.in' => __('Invalid guard selected.'),
        ]);

        try {
            // find permission record
            $permission = Permission::findOrFail($id);

            // update permission values
            $permission->update([
                'name' => $request->name,
                'guard_name' => $request->guard_name,
                'updated_by' => Auth::id(),
            ]);

            // success response
            if ($request->expectsJson()) {
                return response()->json([
                    'success' => true,
                    'message' => __('Permission has been successfully updated.'),
                ]);
            }

            return redirect()->back()->with('success', __('Permission has been successfully updated.'));

        } catch (QueryException $e) {
            if ($request->expectsJson()) {
                return response()->json([
                    'success' => false,
                    'message' => __('Failed to update permission: ').$e->getMessage(),
                ], 500);
            }

            return redirect()->back()->with('error', __('Failed to update permission: ').$e->getMessage());
        }
    }

    // toggle active or inactive status
    public function toggleStatus(Request $request)
    {
        // validate status request
        $request->validate([
            'id' => 'required|exists:permissions,id',
            'status' => 'required|in:activate,deactivate',
        ]);

        $permission = Permission::findOrFail($request->id);

        $permission->update([
            'is_active' => $request->status === 'activate' ? 1 : 0,
            'updated_by' => Auth::id(),
        ]);

        // respond to client (ajax)
        return response()->json([
            'success' => true,
            'message' => __('Permission status updated successfully.'),
        ]);
    }

    public function destroy($id)
    {
        $permission = Permission::findOrFail($id);
        $permission->delete(); // soft delete (not permanent)

        return response()->json([
            'success' => true,
            'message' => __('Permission has been archived.'),
        ]);
    }
}
