<?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\Role;
use App\Models\User;
use App\Support\DataTable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;

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

    /**
     * INDEX — Display user list & roles (for dropdowns)
     */
    public function index(Request $request)
    {
        if (DataTable::isDataTableRequest($request)) {
            return $this->dataTable($request);
        }

        $roles = Role::where('is_active', 1)
            ->whereNull('deleted_at')
            ->orderBy('name')
            ->get();

        return view('pages.access_control.users', compact('roles'));
    }

    protected function dataTable(Request $request)
    {
        try {
            $authUser = $request->user();
            $canManage = $authUser->can('manage user directory');

            $query = User::query()
                ->withTrashed()
                ->with(['roles:id,name', 'creator:id,email', 'updater:id,email']);

            if (! $authUser->hasRole('Developer')) {
                $query->whereDoesntHave('roles', function ($roleQuery) {
                    $roleQuery->where('name', 'Developer');
                });
            }

            // Fast count total
            $recordsTotal = User::withTrashed();
            if (! $authUser->hasRole('Developer')) {
                $recordsTotal->whereDoesntHave('roles', function ($roleQuery) {
                    $roleQuery->where('name', 'Developer');
                });
            }
            $recordsTotal = $recordsTotal->count();
            $globalSearch = DataTable::globalSearch($request);

            if ($canManage) {
                $status = DataTable::columnSearch($request, 1);
                $name = DataTable::columnSearch($request, 2);
                $email = DataTable::columnSearch($request, 3);
                $role = DataTable::columnSearch($request, 4);
                $createdAt = DataTable::columnSearch($request, 5);
                $createdBy = DataTable::columnSearch($request, 6);
                $updatedAt = DataTable::columnSearch($request, 7);
                $updatedBy = DataTable::columnSearch($request, 8);
            } else {
                $status = null;
                $name = DataTable::columnSearch($request, 0);
                $email = DataTable::columnSearch($request, 1);
                $role = DataTable::columnSearch($request, 2);
                $createdAt = DataTable::columnSearch($request, 3);
                $createdBy = DataTable::columnSearch($request, 4);
                $updatedAt = DataTable::columnSearch($request, 5);
                $updatedBy = DataTable::columnSearch($request, 6);
            }

            $trashed = $request->input('trashed');
            if ($trashed === 'archived') {
                $query->onlyTrashed();
            } elseif ($trashed === 'active') {
                $query->withoutTrashed();
            }

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

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

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

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

            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('email', '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 ? 5 : 3, 'desc');

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

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

            $rows = $users->map(function (User $user) use ($authUser, $canManage) {
                $roles = $user->roles->pluck('name')->values();
                $roleBadges = $roles->isNotEmpty()
                    ? $roles->map(fn ($role) => '<span class="badge badge-slate-pill">'.e($role).'</span>')->implode(' ')
                    : '-';

                $row = [];

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

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

                if ($canManage) {
                    $impersonateButton = '';
                    if ($authUser->can('impersonate users')) {
                        if ($authUser->id !== $user->id && ! $user->hasRole('Developer')) {
                            $impersonateButton = '<form method="POST" action="'.route('impersonate.start', $user->id).'" class="d-inline">'
                                .csrf_field()
                                .'<button type="submit" class="btn btn-warning btn-sm rounded-pill px-3">'.e(__('Impersonate')).'</button>'
                                .'</form>';
                        } else {
                            $impersonateButton = '<button type="button" class="btn btn-light btn-sm rounded-pill px-3 text-muted disabled" style="opacity: 0.6; cursor: not-allowed;">'
                                .e(__('Impersonate'))
                                .'</button>';
                        }
                    }

                    if ($user->trashed()) {
                        $row[] = '<div class="d-inline-flex align-items-center gap-2">'
                            .'<button class="btn btn-link text-success btn-restore" data-id="'.$user->id.'" data-name="'.e($user->name).'"><i class="bi bi-arrow-counterclockwise"></i> '.e(__('Restore')).'</button>'
                            .'<button class="btn btn-link text-danger btn-force-delete" data-id="'.$user->id.'" data-name="'.e($user->name).'"><i class="bi bi-x-circle"></i> '.e(__('Terminated')).'</button>'
                            .'</div>';
                    } else {
                        $row[] = '<div class="d-inline-flex align-items-center gap-2">'
                            .$impersonateButton
                            .'<button class="btn btn-link btn-edit" data-id="'.$user->id.'" data-name="'.e($user->name).'" data-email="'.e($user->email).'" data-roles="'.e(json_encode($user->roles->pluck('id')->values()->all())).'" data-created="'.e(format_datetime($user->created_at)).'" data-created-by="'.e($user->creator->email ?? 'System').'" data-updated-at="'.e(format_datetime($user->updated_at)).'" data-updated-by="'.e($user->updater->email ?? '-').'" data-bs-toggle="modal" data-bs-target="#editUserModal"><i class="bi bi-pencil"></i></button>'
                            .'<button class="btn btn-link text-danger btn-delete" data-id="'.$user->id.'" data-name="'.e($user->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 [UserManagement]: '.$e->getMessage());

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

    /**
     * STORE — Tambah user + assign role
     */
    public function store(Request $request)
    {
        $request->validate([
            'name' => [
                'required',
                'string',
                'min:3',
                'max:100',
                'regex:/^[a-zA-Z\s]+$/',
            ],
            'email' => [
                'required',
                'email',
                'max:150',
                Rule::unique('users', 'email'),
            ],
            'password' => [
                'required',
                'string',
                'min:12',
                'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{12,}$/',
            ],
            'roles' => [
                'required',
                'array',
                'min:1',
            ],
            'roles.*' => [
                'required',
                Rule::exists('roles', 'id'),
            ],
        ], [
            'name.regex' => __('Name may only contain letters and spaces.'),
            'email.unique' => __('This email is already registered.'),
            'password.regex' => __('Password must contain uppercase, lowercase, number, and symbol.'),
            'roles.required' => __('At least one role must be selected.'),
        ]);

        try {
            // create user
            $user = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => bcrypt($request->password),
                'is_active' => 1,
                'created_by' => Auth::id(),
            ]);

            // 🔥 FIX: Convert role IDs → role names
            $roleNames = Role::whereIn('id', $request->roles)->pluck('name')->toArray();

            // assign roles by name
            $user->syncRoles($roleNames);

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

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

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

    /**
     * UPDATE — Edit user data + (optional) password + roles
     */
    public function update(Request $request, $id)
    {
        $request->validate([
            'name' => [
                'required',
                'string',
                'min:3',
                'max:100',
                'regex:/^[a-zA-Z\s]+$/',
            ],
            'email' => [
                'required',
                'email',
                'max:150',
                Rule::unique('users', 'email')->ignore($id),
            ],
            'password' => [
                'nullable',
                'string',
                'min:12',
                'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{12,}$/',
            ],
            'roles' => [
                'required',
                'array',
                'min:1',
            ],
            'roles.*' => [
                Rule::exists('roles', 'id'),
            ],
        ], [
            'name.regex' => __('Name may only contain letters and spaces.'),
            'password.regex' => __('Password must contain uppercase, lowercase, number, and symbol.'),
        ]);

        try {
            $user = User::findOrFail($id);

            $data = [
                'name' => $request->name,
                'email' => $request->email,
                'updated_by' => Auth::id(),
            ];

            if ($request->filled('password')) {
                $data['password'] = bcrypt($request->password);
            }

            $user->update($data);

            // 🔥 FIX: Convert ID role → Role Name
            $roleNames = Role::whereIn('id', $request->roles)->pluck('name')->toArray();
            $user->syncRoles($roleNames);

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

            return back()->with('success', __('User updated successfully.'));

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

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

    /**
     * TOGGLE — Aktivasi/Non-aktifkan user
     */
    public function toggleStatus(Request $request)
    {
        $request->validate([
            'id' => 'required|exists:users,id',
            'status' => 'required|in:activate,deactivate',
        ]);

        $user = User::findOrFail($request->id);

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

        return response()->json([
            'success' => true,
            'message' => __('User status updated successfully.'),
        ]);
    }

    /**
     * DESTROY — Archive (Soft Delete)
     */
    public function destroy($id)
    {
        $user = User::findOrFail($id);

        $user->delete();

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

    /**
     * RESTORE — Restore user (Soft Delete)
     */
    public function restore($id)
    {
        $user = User::withTrashed()->findOrFail($id);
        $user->restore();

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

    /**
     * FORCE DELETE — Permanent removal
     */
    public function forceDelete($id)
    {
        $user = User::withTrashed()->findOrFail($id);

        // Prevent accidental deletion of self
        if ($user->id === Auth::id()) {
            return response()->json([
                'success' => false,
                'message' => __('You cannot permanently delete yourself.'),
            ], 403);
        }

        $user->forceDelete();

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

    /**
     * BULK TOGGLE STATUS
     */
    public function bulkToggleStatus(Request $request)
    {
        $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'exists:users,id',
            'status' => 'required|in:activate,deactivate',
        ]);

        $status = $request->status === 'activate' ? 1 : 0;

        User::whereIn('id', $request->ids)->update([
            'is_active' => $status,
            'updated_by' => Auth::id(),
        ]);

        return response()->json([
            'success' => true,
            'message' => __('Selected users have been updated.'),
        ]);
    }

    /**
     * BULK DELETE (Archive)
     */
    public function bulkDelete(Request $request)
    {
        $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'exists:users,id',
        ]);

        User::whereIn('id', $request->ids)->delete();

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

    /**
     * BULK RESTORE
     */
    public function bulkRestore(Request $request)
    {
        $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'exists:users,id',
        ]);

        User::withTrashed()->whereIn('id', $request->ids)->restore();

        return response()->json([
            'success' => true,
            'message' => __('Selected users have been restored.'),
        ]);
    }

    /**
     * BULK FORCE DELETE
     */
    public function bulkForceDelete(Request $request)
    {
        $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'exists:users,id',
        ]);

        // Prevent self deletion
        $ids = array_filter($request->ids, fn ($id) => $id != Auth::id());

        User::withTrashed()->whereIn('id', $ids)->forceDelete();

        return response()->json([
            'success' => true,
            'message' => __('Selected users have been permanently removed.'),
        ]);
    }
}
