<?php

declare(strict_types=1);

namespace App\Http\Controllers\Helpers\Permissions;

use App\Enums\UserTypes;
use App\Models\User;
use Hypervel\Support\Facades\Auth;
use App\Enums\UserActions;


class UserPermissions
{
    use Roles;
    use PermissionsCheck;

    public static function IsParentofTargetUser(string|int $hashkeyORId)
    {
        $currentUser = Auth::user();
        if (!$currentUser) return false;

        $targetUser = is_numeric($hashkeyORId)
            ? User::find($hashkeyORId)
            : User::where('hashkey', $hashkeyORId)->first();

        if (!$targetUser) return false;
        return $targetUser->parentuid === $currentUser->id;
    }

    public static function isAncestorOrFamilyOfTargetUser(string|int $hashkeyORId): bool
    {
        $currentUser = Auth::user();
        if (!$currentUser) return false;

        $targetUser = is_numeric($hashkeyORId)
            ? User::find($hashkeyORId)
            : User::where('hashkey', $hashkeyORId)->first();

        if (!$targetUser) return false;
        if ($currentUser->id === $targetUser->id) return true;
        if ($targetUser->getAllDescendants()->contains($currentUser)) return true;

        $parent = $targetUser->parent;
        while ($parent) {
            if ($parent->id === $currentUser->id) return true;
            $parent = $parent->parent;
        }
        return false;
    }

    public static function isIndirectParentOfTargetUser(string|int $hashkeyORId): bool
    {
        $currentUser = Auth::user();
        if (!$currentUser) return false;

        $targetUser = is_numeric($hashkeyORId)
            ? User::find($hashkeyORId)
            : User::where('hashkey', $hashkeyORId)->first();

        if (!$targetUser) return false;
        if ($currentUser->id === $targetUser->id) return false;

        $descendants = $targetUser->getAllDescendants();
        if ($descendants->contains($currentUser)) {
            if ($targetUser->parentuid === $currentUser->id) return false;
            return true;
        }
        return false;
    }

    private static function safeUserActionFromString(string $value): ?UserActions
    {
        foreach (UserActions::cases() as $case) {
            if ($case->value === $value) return $case;
        }
        return null;
    }

    public static function isDescendantOfCurrentUser(string|int|User|null $hashkeyOrId): bool
    {
        if (!$hashkeyOrId) return false;

        $currentUser = Auth::user();
        if (!$currentUser) return false;

        $targetUser = $hashkeyOrId instanceof User
            ? $hashkeyOrId
            : (is_numeric($hashkeyOrId)
                ? User::find($hashkeyOrId)
                : User::where('hashkey', $hashkeyOrId)->first());

        if (!$targetUser || $currentUser->id === $targetUser->id) return false;

        $descendants = $currentUser->getAllDescendants();
        return $descendants->contains('id', $targetUser->id);
    }

    public static function isActionPermitted(string|int|UserTypes $hashkeyORId, UserActions $userActions)
    {
        $currentUser = Auth::user();
        if (!Auth::check()) return false;

        $currentUserType = $currentUser->acct_type;
        if (!($currentUserType instanceof UserTypes)) {
            $currentUserType = UserTypes::tryFrom($currentUserType) ?? UserTypes::PUBLIC;
        }

        $isDeniedRoles = self::isUserDeniedRoles($userActions);
        if ($isDeniedRoles) return false;

        if (self::CheckifRoleDoesNotRequireaTargetUser($userActions)) {
            $preliminary_permission = true;
        } elseif (($hashkeyORId || $hashkeyORId === 0 || $hashkeyORId === '0') && !($hashkeyORId instanceof UserTypes)) {
            $preliminary_permission = self::isUserPreliminaryPermissionAllowed($hashkeyORId);
        } elseif ($hashkeyORId instanceof UserTypes) {
            $preliminary_permission = self::isUserPreliminaryPermissionAllowed($hashkeyORId);
        } else {
            $preliminary_permission = false;
        }

        if (!$preliminary_permission) return false;

        $permissionString = $userActions->value;
        $permissionEnum = self::safeUserActionFromString($permissionString);
        $allowedThroughAdditionalRoles = self::isUserAllowedbyAdditionalRoles($userActions);

        $isPermissionAllowed = (isset(self::roles()[$currentUserType->value]) &&
            in_array($permissionEnum, self::roles()[$currentUserType->value]));

        return $permissionEnum && ($isPermissionAllowed || $allowedThroughAdditionalRoles);
    }

    private static function isUserPreliminaryPermissionAllowed(string|int|UserTypes $hashkeyORId)
    {
        $currentUser = Auth::user();
        if (!$currentUser) return false;

        if ($currentUser->acct_type === UserTypes::SUPER_ADMIN) return true;

        $currentUserType = $currentUser->acct_type;
        if (!($currentUserType instanceof UserTypes)) {
            $currentUserType = UserTypes::tryFrom($currentUserType) ?? UserTypes::PUBLIC;
        }
        $allowedUserTypes = UserTypeService::getAllowedUserTypes($currentUserType);

        if ($hashkeyORId instanceof UserTypes) {
            $isTypeAllowedtobeModified = in_array($hashkeyORId, $allowedUserTypes);
        } else {
            try {
                $TargetUser = is_string($hashkeyORId)
                    ? User::where('hashkey', $hashkeyORId)->first()
                    : User::where('id', $hashkeyORId)->first();
                $target_acct_type = $TargetUser->acct_type;
                $isTypeAllowedtobeModified = in_array($target_acct_type, $allowedUserTypes);
            } catch (\Throwable $th) {
                throw new \Exception('' . $th->getMessage());
            }
        }

        $IndirectParent = self::isDescendantOfCurrentUser($hashkeyORId);
        $isSelf = $currentUser->hashkey === $hashkeyORId;

        return ($IndirectParent || $isSelf) && $isTypeAllowedtobeModified;
    }

    public static function isUserAllowedbyAdditionalRoles(UserActions $userActions): bool
    {
        $currentUser = Auth::user();
        if (!$currentUser) return false;

        $additionalRoles = $currentUser->additional_roles ?? [];
        if (empty($additionalRoles)) return false;

        foreach ($additionalRoles as $role) {
            if ($role instanceof UserActions) {
                if ($role === $userActions) return true;
            } elseif (is_string($role)) {
                if ($role === $userActions->value) return true;
            }
        }
        return false;
    }

    public static function isUserDeniedRoles(UserActions $userActions)
    {
        $currentUser = User::findOrFail(Auth::id());
        if (!$currentUser) return false;

        if ($currentUser->acct_type === UserTypes::SUPER_ADMIN) return false;

        $currentUserAdditionalRoles = $currentUser->denied_roles ?? [];
        if (empty($currentUserAdditionalRoles)) return false;

        return in_array($userActions->value, $currentUserAdditionalRoles);
    }

    public static function getUserRoles(int $id)
    {
        try {
            $currentUser = User::findOrFail($id);
        } catch (\Throwable $th) {
            return false;
        }

        $acct_type = $currentUser->acct_type;
        $defaultuserRoles = self::roles()[$acct_type->value] ?? [];
        $additionalRoles = $currentUser->additional_roles ?? [];
        $deniedRoles = $currentUser->denied_roles ?? [];
        $mergedRoles = array_merge($defaultuserRoles, $additionalRoles);
        $uniqueRoles = [];
        foreach ($mergedRoles as $role) {
            $uniqueRoles[$role->value] = $role;
        }
        foreach ($deniedRoles as $denied) {
            unset($uniqueRoles[$denied->value]);
        }
        return array_values($uniqueRoles);
    }

    public static function normalizeRole(UserActions|string $role): UserActions
    {
        if ($role instanceof UserActions) return $role;
        if ($e = UserActions::tryFrom($role)) return $e;
        foreach (UserActions::cases() as $case) {
            if ($case->name === $role) return $case;
        }
        $snake = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $role));
        if ($e = UserActions::tryFrom($snake)) return $e;
        throw new \InvalidArgumentException("Unknown role: {$role}");
    }

    public static function isAncestorOf(User|int|string $ancestor, User|int|string $descendant): bool
    {
        if (!$ancestor || !$descendant) return false;

        $ancestorUser = $ancestor instanceof User ? $ancestor : (is_numeric($ancestor) ? User::find((int)$ancestor) : User::where('hashkey', $ancestor)->first());
        $descendantUser = $descendant instanceof User ? $descendant : (is_numeric($descendant) ? User::find((int)$descendant) : User::where('hashkey', $descendant)->first());

        if (!$ancestorUser || !$descendantUser) return false;
        if ($ancestorUser->id === $descendantUser->id) return true;

        $parent = $descendantUser->parent;
        while ($parent) {
            if ($parent->id === $ancestorUser->id) return true;
            $parent = $parent->parent;
        }
        return false;
    }
}


trait PermissionsCheck
{
    public static function isUserModificationAllowed(string|int $hashkeyORId): bool
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ModifyUser);
    }

    public static function isUserSetActiveAllowed(string|int $hashkeyORId): bool
    {
        return self::isActionPermitted($hashkeyORId, UserActions::SetActiveUser);
    }

    public static function isUserSetInactiveAllowed(string|int $hashkeyORId): bool
    {
        return self::isActionPermitted($hashkeyORId, UserActions::SetInActiveUser);
    }

    public static function isUserDeletionAllowed(string|int $hashkeyORId): bool
    {
        return self::isActionPermitted($hashkeyORId, UserActions::DeleteUser);
    }

    public static function isUserExecChangeAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::UpdateUserExec);
    }

    public static function isUserExecViewingAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ViewUserExec);
    }

    public static function isUserExecDeletionAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::DeleteUserExec);
    }

    public static function isUserNotesViewingAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ViewUserNotes);
    }

    public static function isUserNotesUpdateAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::SetUserNotes);
    }

    public static function isUserNotesDeletionAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::DeleteUserNotes);
    }

    public static function isUserPasswordChangeAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ChangeUserPassword);
    }

    public static function isForceLogoutUserAllowed(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ForceLogoutUser);
    }

    public static function isUserAllowedtoViewAnotherUserRoles(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::UserAllowedtoChangeAnotherUserRoles);
    }

    public static function isUserAllowedtoViewSelfRoles(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::UserAllowedtoViewSelfRoles);
    }

    public static function isUserAllowedtoChangeAnotherUserRoles(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::UserAllowedtoChangeAnotherUserRoles);
    }

    public function isUserAllowedtoChangeParent(string|int $hashkeyORId)
    {
        return self::isActionPermitted($hashkeyORId, UserActions::ChangeAnotherUsersParent);
    }
}


trait Roles
{
    public static $RoleswithNoTargetUser = [
        UserActions::ViewAllUserTypes,
        UserActions::ListAllUsersAsParentforUserCreation,
        UserActions::CheckifMobileNumberExists,
        UserActions::CheckifUsernameExists,
        UserActions::ViewAllFiles,
        UserActions::UploadAllFiles,
        UserActions::DeleteAllFiles,
        UserActions::ModifyAllFiles,
        UserActions::ViewGlobalReports,
        UserActions::CreateAnnouncement,
        UserActions::ModifyAnnouncement,
        UserActions::DeleteAnnouncement,
        UserActions::ViewAllAnnouncements,
        UserActions::ViewAccountingReports,
        UserActions::ManageAccounting,
        UserActions::UltimateConsole,
        UserActions::UltimateLogs,
        UserActions::UltimateReports,
        UserActions::UltimateMaintenance,
        UserActions::UltimateQuery,
        UserActions::UltimateGlobalMessage,
        UserActions::UltimateFlush,
        UserActions::ManageLandingPages,
        UserActions::ManageQrphPaymentCode,
        UserActions::ViewChapterOrgChart,
        UserActions::ManageChapterMembers,
        UserActions::ViewScopedMemberReports,
        UserActions::AssignChapterOfficer,
        UserActions::ViewResidents,
        UserActions::ManageResidents,
        UserActions::ImportResidents,
        UserActions::ExportResidents,
        UserActions::ViewHouseholds,
        UserActions::ManageHouseholds,
        UserActions::ViewDocumentRequests,
        UserActions::CreateDocumentRequest,
        UserActions::ProcessDocumentRequest,
        UserActions::ManageRequestTypes,
        UserActions::ManageFeeSchedules,
        UserActions::ViewBlotters,
        UserActions::CreateBlotter,
        UserActions::ProcessBlotter,
        UserActions::ManageBlotterHearings,
        UserActions::ViewBarangayProjects,
        UserActions::ManageBarangayProjects,
        UserActions::ViewBarangayBudget,
        UserActions::ManageBarangayBudget,
        UserActions::ViewFeePayments,
        UserActions::ManageFeePayments,
    ];

    public static function CheckifRoleDoesNotRequireaTargetUser(UserActions $userAction): bool
    {
        return in_array($userAction, self::$RoleswithNoTargetUser, true);
    }

    public static function roles()
    {
        // Barangay staff actions shared by most staff roles
        $staffReadActions = [
            UserActions::ViewResidents,
            UserActions::ViewHouseholds,
            UserActions::ViewBlotters,
            UserActions::ViewDocumentRequests,
            UserActions::ViewBarangayProjects,
            UserActions::ViewBarangayBudget,
            UserActions::ViewAllAnnouncements,
            UserActions::ViewAllFiles,
            UserActions::ViewUserInfo,
            UserActions::ViewChapterOrgChart,
            UserActions::ViewFeePayments,
            UserActions::ViewAllUserTypes,
            UserActions::ListAllUsersAsParentforUserCreation,
            UserActions::CheckifMobileNumberExists,
            UserActions::CheckifUsernameExists,
        ];

        $staffManageActions = array_merge($staffReadActions, [
            UserActions::ManageResidents,
            UserActions::ImportResidents,
            UserActions::ExportResidents,
            UserActions::ManageHouseholds,
            UserActions::CreateBlotter,
            UserActions::ProcessBlotter,
            UserActions::ManageBlotterHearings,
            UserActions::ProcessDocumentRequest,
            UserActions::ManageRequestTypes,
            UserActions::ManageFeeSchedules,
            UserActions::ManageBarangayProjects,
            UserActions::ManageBarangayBudget,
            UserActions::ManageFeePayments,
            UserActions::CreateAnnouncement,
            UserActions::ModifyAnnouncement,
            UserActions::DeleteAnnouncement,
            UserActions::UploadAllFiles,
            UserActions::ModifyAllFiles,
            UserActions::DeleteAllFiles,
            UserActions::ManageUserInfo,
            UserActions::ManageChapterMembers,
            UserActions::AssignChapterOfficer,
        ]);

        return [
            // Super admin gets everything
            UserTypes::SUPER_ADMIN->value => UserActions::cases(),

            // Punong Barangay - full management
            UserTypes::PUNONG_BARANGAY->value => array_merge($staffManageActions, [
                UserActions::UltimateConsole,
                UserActions::UltimateLogs,
                UserActions::UltimateMaintenance,
                UserActions::UltimateGlobalMessage,
                UserActions::UltimateFlush,
                UserActions::UltimateReports,
                UserActions::ManageQrphPaymentCode,
                UserActions::ManageLandingPages,
                UserActions::ModifyGlobalReports,
                UserActions::ViewGlobalReports,
                UserActions::ViewAccountingReports,
                UserActions::ManageAccounting,
                UserActions::ViewScopedMemberReports,
                UserActions::CreateUser,
                UserActions::ModifyUser,
                UserActions::SetActiveUser,
                UserActions::SetInActiveUser,
                UserActions::ChangeUserPassword,
                UserActions::ForceLogoutUser,
                UserActions::UserAllowedtoChangeAnotherUserRoles,
                UserActions::UserAllowedtoViewOtherUserRoles,
                UserActions::UserAllowedtoViewAllRoles,
                UserActions::ChangeAnotherUsersParent,
                UserActions::ViewAllUserTypes,
                UserActions::CreateUserSecretary,
                UserActions::CreateUserTreasurer,
                UserActions::CreateUserKagawad,
                UserActions::CreateUserSkChairperson,
                UserActions::CreateUserSkCouncilor,
                UserActions::CreateUserTanod,
                UserActions::CreateUserBhw,
                UserActions::CreateUserDaycareWorker,
                UserActions::CreateUserStaff,
                UserActions::CreateUserResident,
                UserActions::CreateUserAudit,
            ]),

            // Kagawad - manage operations, limited admin
            UserTypes::KAGAWAD->value => array_merge($staffManageActions, [
                UserActions::ViewGlobalReports,
                UserActions::ViewAccountingReports,
                UserActions::ViewScopedMemberReports,
                UserActions::CreateUserResident,
            ]),

            // Secretary - records & documents focus
            UserTypes::SECRETARY->value => array_merge($staffManageActions, [
                UserActions::ViewGlobalReports,
                UserActions::CreateUserResident,
                UserActions::ModifyUser,
                UserActions::SetActiveUser,
                UserActions::SetInActiveUser,
            ]),

            // Treasurer - budget & payments focus
            UserTypes::TREASURER->value => array_merge($staffReadActions, [
                UserActions::ViewBarangayBudget,
                UserActions::ManageBarangayBudget,
                UserActions::ViewFeePayments,
                UserActions::ManageFeePayments,
                UserActions::ViewDocumentRequests,
                UserActions::ProcessDocumentRequest,
                UserActions::ViewAccountingReports,
                UserActions::ManageAccounting,
                UserActions::ManageQrphPaymentCode,
                UserActions::UploadAllFiles,
                UserActions::ModifyAllFiles,
            ]),

            // SK Chairperson - youth & projects
            UserTypes::SK_CHAIRPERSON->value => array_merge($staffReadActions, [
                UserActions::ManageBarangayProjects,
                UserActions::CreateAnnouncement,
                UserActions::ModifyAnnouncement,
                UserActions::UploadAllFiles,
                UserActions::CreateUserSkCouncilor,
                UserActions::CreateUserResident,
            ]),

            // SK Councilor - limited support
            UserTypes::SK_COUNCILOR->value => array_merge($staffReadActions, [
                UserActions::CreateAnnouncement,
                UserActions::UploadAllFiles,
            ]),

            // Tanod - blotter & security
            UserTypes::TANOD->value => array_merge($staffReadActions, [
                UserActions::CreateBlotter,
                UserActions::ProcessBlotter,
                UserActions::ManageBlotterHearings,
                UserActions::UploadAllFiles,
            ]),

            // BHW - residents & health
            UserTypes::BHW->value => array_merge($staffReadActions, [
                UserActions::ManageResidents,
                UserActions::ManageHouseholds,
                UserActions::UploadAllFiles,
            ]),

            // Daycare Worker - basic read + residents
            UserTypes::DAYCARE_WORKER->value => array_merge($staffReadActions, [
                UserActions::ManageResidents,
                UserActions::UploadAllFiles,
            ]),

            // Staff - general operations
            UserTypes::STAFF->value => array_merge($staffReadActions, [
                UserActions::ProcessDocumentRequest,
                UserActions::CreateBlotter,
                UserActions::UploadAllFiles,
                UserActions::CreateUserResident,
            ]),

            // Resident - self-service only
            UserTypes::RESIDENT->value => [
                UserActions::CreateDocumentRequest,
                UserActions::ViewDocumentRequests,
                UserActions::ViewAllAnnouncements,
                UserActions::ViewUserInfo,
                UserActions::ManageUserInfo,
                UserActions::UserAllowedtoViewSelfRoles,
                UserActions::CheckifMobileNumberExists,
                UserActions::CheckifUsernameExists,
                UserActions::UploadAllFiles,
            ],

            // Audit - read-only across the system
            UserTypes::AUDIT->value => array_merge($staffReadActions, [
                UserActions::ViewGlobalReports,
                UserActions::ViewAccountingReports,
                UserActions::ViewScopedMemberReports,
            ]),
        ];
    }
}


class UserTypeService
{
    public static function getAllowedUserTypes(UserTypes $currentUserType): array
    {
        return match ($currentUserType) {
            UserTypes::SUPER_ADMIN => UserTypes::cases(),

            UserTypes::PUNONG_BARANGAY => [
                UserTypes::KAGAWAD,
                UserTypes::SECRETARY,
                UserTypes::TREASURER,
                UserTypes::SK_CHAIRPERSON,
                UserTypes::SK_COUNCILOR,
                UserTypes::TANOD,
                UserTypes::BHW,
                UserTypes::DAYCARE_WORKER,
                UserTypes::STAFF,
                UserTypes::RESIDENT,
                UserTypes::AUDIT,
            ],

            UserTypes::KAGAWAD, UserTypes::SECRETARY => [
                UserTypes::RESIDENT,
            ],

            UserTypes::SK_CHAIRPERSON => [
                UserTypes::SK_COUNCILOR,
                UserTypes::RESIDENT,
            ],

            default => [],
        };
    }
}
