<?php

namespace App\Helpers;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

/**
 * Profile Authorization Helper
 *
 * Provides centralized authorization validation for profile operations.
 * Prevents IDOR (Insecure Direct Object Reference) vulnerabilities by
 * validating that authenticated users have permission to act on profiles.
 */
class ProfileAuthorizationHelper
{
    /**
     * Get authenticated profile from any guard (multi-guard support).
     * Returns the authenticated model (User, Organization, Bank, or Admin).
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    private static function getAuthenticatedProfile()
    {
        // Check all guards and return the authenticated model
        return Auth::guard('admin')->user()
            ?: Auth::guard('bank')->user()
            ?: Auth::guard('organization')->user()
            ?: Auth::guard('web')->user();
    }

    /**
     * Validate that the authenticated user has ownership/access to a profile.
     *
     * This function prevents IDOR attacks by ensuring:
     * - Users can only access their own User profile
     * - Users can only access Organizations they're linked to
     * - Users can only access Banks they're linked to
     * - Users can only access Admin profiles they're linked to
     *
     * @param mixed $profile The profile to validate (User, Organization, Bank, or Admin)
     * @param bool $throwException Whether to throw 403 exception (default: true)
     * @return bool True if authorized, false if not (when $throwException = false)
     * @throws \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public static function validateProfileOwnership($profile, bool $throwException = true): bool
    {
        $authenticatedProfile = self::getAuthenticatedProfile();

        // Must be authenticated
        if (!$authenticatedProfile) {
            Log::warning('ProfileAuthorizationHelper: Attempted profile access without authentication', [
                'profile_id' => $profile?->id,
                'profile_type' => $profile ? get_class($profile) : null,
            ]);

            if ($throwException) {
                abort(401, 'Authentication required');
            }
            return false;
        }

        // IMPORTANT: Verify guard matches profile type to prevent cross-guard attacks
        // Even if the user has a relationship with the profile, they must be authenticated on the correct guard
        $expectedGuardForProfile = null;
        if ($profile instanceof \App\Models\Bank) {
            $expectedGuardForProfile = 'bank';
        } elseif ($profile instanceof \App\Models\Organization) {
            $expectedGuardForProfile = 'organization';
        } elseif ($profile instanceof \App\Models\Admin) {
            $expectedGuardForProfile = 'admin';
        } elseif ($profile instanceof \App\Models\User) {
            $expectedGuardForProfile = 'web';
        }

        // Check which guard the current authentication is from
        $currentGuard = null;
        if (Auth::guard('admin')->check() && Auth::guard('admin')->user() === $authenticatedProfile) {
            $currentGuard = 'admin';
        } elseif (Auth::guard('bank')->check() && Auth::guard('bank')->user() === $authenticatedProfile) {
            $currentGuard = 'bank';
        } elseif (Auth::guard('organization')->check() && Auth::guard('organization')->user() === $authenticatedProfile) {
            $currentGuard = 'organization';
        } elseif (Auth::guard('web')->check() && Auth::guard('web')->user() === $authenticatedProfile) {
            $currentGuard = 'web';
        }

        // Prevent cross-guard access
        if ($expectedGuardForProfile && $currentGuard && $expectedGuardForProfile !== $currentGuard) {
            Log::warning('ProfileAuthorizationHelper: Cross-guard access attempt blocked', [
                'authenticated_guard' => $currentGuard,
                'target_profile_type' => get_class($profile),
                'expected_guard' => $expectedGuardForProfile,
                'profile_id' => $profile->id,
            ]);

            if ($throwException) {
                abort(403, 'Unauthorized: Cannot access ' . class_basename($profile) . ' profile from ' . $currentGuard . ' guard');
            }
            return false;
        }

        // Check if authenticated profile is same type and ID as target profile (direct match)
        if (get_class($authenticatedProfile) === get_class($profile) && $authenticatedProfile->id === $profile->id) {
            // User is accessing their own profile of same type
            Log::info('ProfileAuthorizationHelper: Direct profile access authorized', [
                'profile_type' => get_class($profile),
                'profile_id' => $profile->id,
            ]);
            return true;
        }

        // For cross-profile access, we need to check relationships via User model
        // Get the underlying User for relationship checks
        $authenticatedUser = null;
        if ($authenticatedProfile instanceof \App\Models\User) {
            $authenticatedUser = $authenticatedProfile;
        } elseif ($authenticatedProfile instanceof \App\Models\Admin) {
            // Admin can access if they ARE the target admin (already checked above)
            // For other profile types, need to get linked users
            if (!($profile instanceof \App\Models\Admin)) {
                // Admin trying to access non-admin profile - get one of the linked users
                $authenticatedUser = $authenticatedProfile->users()->first();
            }
        } elseif ($authenticatedProfile instanceof \App\Models\Organization) {
            // Organization can access if they ARE the target org (already checked above)
            // For other profile types, need to get linked users
            if (!($profile instanceof \App\Models\Organization)) {
                $authenticatedUser = $authenticatedProfile->users()->first();
            }
        } elseif ($authenticatedProfile instanceof \App\Models\Bank) {
            // Bank can access if they ARE the target bank (already checked above)
            // For other profile types, need to get linked users
            if (!($profile instanceof \App\Models\Bank)) {
                $authenticatedUser = $authenticatedProfile->users()->first();
            }
        }

        // If we couldn't get a User for relationship checking and it's not a direct match, deny
        if (!$authenticatedUser) {
            if ($throwException) {
                abort(403, 'Unauthorized: Cannot validate cross-profile access');
            }
            return false;
        }

        // Validate based on target profile type
        if ($profile instanceof \App\Models\User) {
            // User can only access their own user profile
            if ($profile->id !== $authenticatedUser->id) {
                Log::warning('ProfileAuthorizationHelper: Unauthorized User profile access attempt', [
                    'authenticated_user_id' => $authenticatedUser->id,
                    'target_user_id' => $profile->id,
                ]);

                if ($throwException) {
                    abort(403, 'Unauthorized: You cannot access another user\'s profile');
                }
                return false;
            }

        } elseif ($profile instanceof \App\Models\Organization) {
            // User must be linked to this organization
            if (!$authenticatedUser->organizations()->where('organization_user.organization_id', $profile->id)->exists()) {
                Log::warning('ProfileAuthorizationHelper: Unauthorized Organization access attempt', [
                    'authenticated_user_id' => $authenticatedUser->id,
                    'target_organization_id' => $profile->id,
                    'user_organizations' => $authenticatedUser->organizations()->pluck('organizations.id')->toArray(),
                ]);

                if ($throwException) {
                    abort(403, 'Unauthorized: You are not linked to this organization');
                }
                return false;
            }

        } elseif ($profile instanceof \App\Models\Bank) {
            // User must be linked to this bank
            if (!$authenticatedUser->banksManaged()->where('bank_user.bank_id', $profile->id)->exists()) {
                Log::warning('ProfileAuthorizationHelper: Unauthorized Bank access attempt', [
                    'authenticated_user_id' => $authenticatedUser->id,
                    'target_bank_id' => $profile->id,
                    'user_banks' => $authenticatedUser->banksManaged()->pluck('banks.id')->toArray(),
                ]);

                if ($throwException) {
                    abort(403, 'Unauthorized: You are not linked to this bank');
                }
                return false;
            }

        } elseif ($profile instanceof \App\Models\Admin) {
            // User must be linked to this admin profile
            if (!$authenticatedUser->admins()->where('admin_user.admin_id', $profile->id)->exists()) {
                Log::warning('ProfileAuthorizationHelper: Unauthorized Admin access attempt', [
                    'authenticated_user_id' => $authenticatedUser->id,
                    'target_admin_id' => $profile->id,
                    'user_admins' => $authenticatedUser->admins()->pluck('admins.id')->toArray(),
                ]);

                if ($throwException) {
                    abort(403, 'Unauthorized: You are not linked to this admin profile');
                }
                return false;
            }

        } else {
            // Unknown profile type
            Log::error('ProfileAuthorizationHelper: Unknown profile type', [
                'profile_type' => get_class($profile),
                'profile_id' => $profile?->id,
            ]);

            if ($throwException) {
                abort(500, 'Unknown profile type');
            }
            return false;
        }

        // Authorization successful
        Log::info('ProfileAuthorizationHelper: Profile access authorized', [
            'authenticated_user_id' => $authenticatedUser->id,
            'profile_type' => get_class($profile),
            'profile_id' => $profile->id,
        ]);

        return true;
    }

    /**
     * Validate profile ownership and throw exception if unauthorized.
     *
     * Convenience method for the most common use case.
     *
     * @param mixed $profile The profile to validate
     * @return void
     * @throws \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public static function authorize($profile): void
    {
        self::validateProfileOwnership($profile, true);
    }

    /**
     * Check if user has access to profile without throwing exception.
     *
     * @param mixed $profile The profile to check
     * @return bool True if authorized, false otherwise
     */
    public static function can($profile): bool
    {
        return self::validateProfileOwnership($profile, false);
    }

    /**
     * Check if the web-authenticated user owns a profile (for profile switching).
     *
     * This method is specifically for profile switching and does NOT enforce guard matching
     * since during a switch, the user is authenticated on 'web' guard but wants to access
     * an elevated profile (Admin, Bank, Organization).
     *
     * @param mixed $profile The profile to check ownership of
     * @return bool True if the web-authenticated user owns this profile
     */
    public static function userOwnsProfile($profile): bool
    {
        $user = Auth::guard('web')->user();

        if (!$user || !$profile) {
            return false;
        }

        // Check based on profile type
        if ($profile instanceof \App\Models\User) {
            return $profile->id === $user->id;
        } elseif ($profile instanceof \App\Models\Organization) {
            return $user->organizations()->where('organization_user.organization_id', $profile->id)->exists();
        } elseif ($profile instanceof \App\Models\Bank) {
            return $user->banksManaged()->where('bank_user.bank_id', $profile->id)->exists();
        } elseif ($profile instanceof \App\Models\Admin) {
            return $user->admins()->where('admin_user.admin_id', $profile->id)->exists();
        }

        return false;
    }
}
