<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Http\Requests\PasswordLoginRequest;
use App\Http\Requests\RequestOtpRequest;
use App\Http\Requests\VerifyOtpRequest;
use App\Models\User;
use App\Services\CartService;
use App\Services\OtpService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;

class OtpLoginController extends Controller
{
    public function __construct(
        protected OtpService $otpService,
        protected CartService $cartService
    ) {
        $this->middleware('guest')->except(['logout', 'getPublicKey']);
    }

    public function showLoginForm(Request $request): View
    {
        return view('auth.otp-login');
    }

    public function switchMode(Request $request): RedirectResponse
    {
        $mode = $request->input('mode', 'otp');
        return redirect()->route('login')->with('login_mode', $mode);
    }

    public function loginWithPassword(PasswordLoginRequest $request): RedirectResponse
    {
        $credentials = $request->validated();
        
        // Normalize mobile (strip +91 prefix) for database lookup
        $mobile = preg_replace('/^\+91/', '', $credentials['mobile']);

        $user = User::where('mobile', $mobile)->first();

        if (!$user) {
            return redirect()->back()->withErrors([
                'mobile' => 'No account found with this mobile number.',
            ])->withInput(['mobile' => $mobile]);
        }

        if (!$user->password) {
            return redirect()->back()->withErrors([
                'password' => 'Password not set. Please use OTP login or reset your password.',
            ])->withInput(['mobile' => $mobile]);
        }

        // Verify password directly since we already have the user
        if (!Hash::check($credentials['password'], $user->password)) {
            return redirect()->back()->withErrors([
                'password' => 'Invalid password.',
            ])->withInput(['mobile' => $mobile]);
        }

        // Check if user is banned
        if ($user->isBanned()) {
            return redirect()->back()->withErrors([
                'mobile' => 'Your account has been banned. Please contact support.',
            ])->withInput(['mobile' => $mobile]);
        }

        // Log the user in manually
        Auth::login($user, $request->filled('remember'));
        
        $user->update(['last_login_at' => now()]);

        // Merge guest cart with user cart after login
        $cookieToken = $request->cookie('cart_token');
        if ($cookieToken) {
            $this->cartService->mergeGuestCartWithUser($user, $cookieToken);
        }

        return redirect()->intended(route('home'));
    }

    public function requestOtp(RequestOtpRequest $request): RedirectResponse
    {
        $mobile = $request->validated('mobile');
        
        // Normalize mobile (strip +91 prefix) for storage and lookup
        $normalizedMobile = preg_replace('/^\+91/', '', $mobile);

        $otp = $this->otpService->generateOtp();
        $sent = $this->otpService->sendLoginOtp($normalizedMobile, $otp);

        if (!$sent) {
            return redirect()->back()->with('error', 'Failed to send OTP. Please try again.');
        }

        $this->otpService->storeOtp($normalizedMobile, $otp);

        return redirect()->back()->with([
            'success' => 'OTP sent to your mobile number.',
            'mobile' => $normalizedMobile,
            'show_verify' => true,
        ]);
    }

    public function verifyOtp(VerifyOtpRequest $request): RedirectResponse
    {
        $data = $request->validated();
        
        // Normalize mobile (strip +91 prefix) for database lookup
        $normalizedMobile = preg_replace('/^\+91/', '', $data['mobile']);

        $verified = $this->otpService->verifyOtp($normalizedMobile, $data['otp']);

        if (!$verified) {
            return redirect()->back()->with([
                'error' => 'Invalid or expired OTP.',
                'mobile' => $normalizedMobile,
                'show_verify' => true,
            ]);
        }

        // Find or create user
        $user = User::firstOrCreate(
            ['mobile' => $normalizedMobile],
            ['name' => $normalizedMobile] // Default name, can be updated later
        );

        $user->update(['last_login_at' => now()]);

        auth()->login($user, true);

        // Merge guest cart with user cart after login
        $cookieToken = $request->cookie('cart_token');
        if ($cookieToken) {
            $this->cartService->mergeGuestCartWithUser($user, $cookieToken);
        }

        return redirect()->intended(route('home'));
    }

    public function logout(): RedirectResponse
    {
        auth()->logout();

        request()->session()->invalidate();
        request()->session()->regenerateToken();

        return redirect()->route('home')->with('success', 'Logged out successfully.');
    }

    /**
     * Get RSA public key for client-side encryption
     */
    public function getPublicKey(): JsonResponse
    {
        try {
            $publicKeyPath = storage_path('app/rsa_public.pem');
            
            // Generate keys if they don't exist
            if (!file_exists($publicKeyPath)) {
                $this->generateRsaKeys();
            }
            
            if (!file_exists($publicKeyPath)) {
                \Log::error('RSA public key file not found after generation attempt');
                return response()->json([
                    'error' => 'Failed to generate RSA keys. Please contact support.'
                ], 500);
            }
            
            $publicKey = file_get_contents($publicKeyPath);
            
            if (empty($publicKey)) {
                \Log::error('RSA public key file is empty');
                return response()->json([
                    'error' => 'RSA public key is empty. Please contact support.'
                ], 500);
            }
            
            // Return the full PEM format - JSEncrypt can handle it
            return response()->json([
                'public_key' => $publicKey
            ]);
            
        } catch (\Exception $e) {
            \Log::error('Error getting RSA public key: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'error' => 'Failed to load encryption key: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Generate RSA key pair
     */
    private function generateRsaKeys(): void
    {
        // Check if OpenSSL extension is available
        if (!extension_loaded('openssl')) {
            throw new \RuntimeException('OpenSSL extension is not loaded');
        }

        // Clear any previous OpenSSL errors
        while (openssl_error_string() !== false) {
            // Clear errors
        }

        // Create OpenSSL configuration for Windows compatibility
        $config = [
            'digest_alg' => 'sha256',
            'private_key_bits' => 2048,
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
        ];

        // Try to find or create OpenSSL config file (for Windows)
        $opensslConfigFile = $this->findOrCreateOpenSslConfig();
        if ($opensslConfigFile) {
            $config['config'] = $opensslConfigFile;
        }

        $res = openssl_pkey_new($config);
        if (!$res) {
            $errors = [];
            while (($error = openssl_error_string()) !== false) {
                $errors[] = $error;
            }
            $errorMsg = !empty($errors) ? implode('; ', $errors) : 'Unknown error';
            throw new \RuntimeException('Failed to generate RSA key pair: ' . $errorMsg);
        }

        $exportResult = openssl_pkey_export($res, $privateKey, null, $config);
        if (!$exportResult) {
            $errors = [];
            while (($error = openssl_error_string()) !== false) {
                $errors[] = $error;
            }
            $errorMsg = !empty($errors) ? implode('; ', $errors) : 'Unknown error';
            throw new \RuntimeException('Failed to export private key: ' . $errorMsg);
        }

        $publicKeyDetails = openssl_pkey_get_details($res);
        if (!$publicKeyDetails || !isset($publicKeyDetails['key'])) {
            throw new \RuntimeException('Failed to get public key details');
        }
        
        $publicKey = $publicKeyDetails['key'];

        // Ensure storage/app directory exists
        $storagePath = storage_path('app');
        if (!is_dir($storagePath)) {
            if (!mkdir($storagePath, 0755, true)) {
                throw new \RuntimeException('Failed to create storage directory');
            }
        }

        // Write keys to files
        $privateKeyPath = storage_path('app/rsa_private.pem');
        $publicKeyPath = storage_path('app/rsa_public.pem');
        
        if (file_put_contents($privateKeyPath, $privateKey) === false) {
            throw new \RuntimeException('Failed to write private key file');
        }
        
        if (file_put_contents($publicKeyPath, $publicKey) === false) {
            throw new \RuntimeException('Failed to write public key file');
        }
        
        // Set proper permissions (Windows may not support chmod, so ignore errors)
        @chmod($privateKeyPath, 0600);
        @chmod($publicKeyPath, 0644);
    }

    /**
     * Find or create OpenSSL configuration file
     */
    private function findOrCreateOpenSslConfig(): ?string
    {
        // Common locations for OpenSSL config on Windows
        $possiblePaths = [
            'C:/wamp64/bin/php/php*/extras/ssl/openssl.cnf',
            'C:/wamp/bin/php/php*/extras/ssl/openssl.cnf',
            'C:/xampp/php/extras/openssl/openssl.cnf',
            ini_get('openssl.cnf'),
        ];

        foreach ($possiblePaths as $path) {
            if (empty($path)) {
                continue;
            }

            // Handle glob patterns
            if (strpos($path, '*') !== false) {
                $matches = glob($path);
                if (!empty($matches)) {
                    $path = $matches[0];
                } else {
                    continue;
                }
            }

            if (file_exists($path) && is_readable($path)) {
                return $path;
            }
        }

        // Try to get from PHP ini
        $iniPath = ini_get('openssl.cnf');
        if ($iniPath && file_exists($iniPath)) {
            return $iniPath;
        }

        // Create a minimal OpenSSL config file in storage if none found
        $configPath = storage_path('app/openssl.cnf');
        if (!file_exists($configPath)) {
            $minimalConfig = <<<'CONFIG'
# Minimal OpenSSL configuration for RSA key generation
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
CONFIG;
            
            if (file_put_contents($configPath, $minimalConfig) !== false) {
                return $configPath;
            }
        } else {
            return $configPath;
        }

        // Return null if not found and couldn't create (OpenSSL will try to use defaults)
        return null;
    }

    /**
     * Decrypt RSA encrypted password
     */
    private function decryptPassword(string $encryptedPassword): string
    {
        $privateKeyPath = storage_path('app/rsa_private.pem');
        
        if (!file_exists($privateKeyPath)) {
            \Log::error('RSA private key not found at: ' . $privateKeyPath);
            throw new \RuntimeException('RSA private key not found. This may indicate a server migration issue. Please copy rsa_private.pem from old server to storage/app/ directory.');
        }

        $privateKey = file_get_contents($privateKeyPath);
        $decrypted = '';
        
        // JSEncrypt sends base64 encoded string, decode it
        $encrypted = base64_decode($encryptedPassword, true);
        
        if ($encrypted === false) {
            // If base64 decode fails, try using the string directly
            $encrypted = $encryptedPassword;
        }
        
        if (!openssl_private_decrypt($encrypted, $decrypted, $privateKey)) {
            $errors = [];
            while (($error = openssl_error_string()) !== false) {
                $errors[] = $error;
            }
            $errorMsg = !empty($errors) ? implode('; ', $errors) : 'Unknown decryption error';
            \Log::error('RSA Decryption failed', [
                'error' => $errorMsg,
                'encrypted_length' => strlen($encryptedPassword),
                'key_exists' => file_exists($privateKeyPath)
            ]);
            
            // Check if this might be a key mismatch issue (common after migration)
            if (strpos($errorMsg, 'bad decrypt') !== false || strpos($errorMsg, 'padding') !== false) {
                throw new \RuntimeException('Password decryption failed. This may indicate RSA key mismatch after server migration. Please copy rsa_private.pem and rsa_public.pem from old server to storage/app/ directory.');
            }
            
            throw new \RuntimeException('Failed to decrypt password: ' . $errorMsg);
        }

        return $decrypted;
    }

    /**
     * API: Login with password (JSON response)
     */
    public function loginWithPasswordApi(Request $request): JsonResponse
    {
        try {
            $request->validate([
                'mobile' => ['required', 'string', 'regex:/^[6-9]\d{9}$/'],
                'encrypted_password' => ['required', 'string'],
                'remember' => ['sometimes', 'boolean'],
            ]);

            // Decrypt password
            $password = $this->decryptPassword($request->input('encrypted_password'));
            
            // Normalize mobile
            $mobile = preg_replace('/^\+91/', '', $request->input('mobile'));

            $user = User::where('mobile', $mobile)->first();

            if (!$user) {
                return response()->json([
                    'success' => false,
                    'errors' => ['mobile' => ['No account found with this mobile number.']]
                ], 422);
            }

            if (!$user->password) {
                return response()->json([
                    'success' => false,
                    'errors' => ['password' => ['Password not set. Please use OTP login or reset your password.']]
                ], 422);
            }

            if (!Hash::check($password, $user->password)) {
                return response()->json([
                    'success' => false,
                    'errors' => ['password' => ['Invalid password.']]
                ], 422);
            }

            if ($user->isBanned()) {
                return response()->json([
                    'success' => false,
                    'errors' => ['mobile' => ['Your account has been banned. Please contact support.']]
                ], 422);
            }

            Auth::login($user, $request->filled('remember'));
            $user->update(['last_login_at' => now()]);

            // Merge guest cart
            $cookieToken = $request->cookie('cart_token');
            if ($cookieToken) {
                $this->cartService->mergeGuestCartWithUser($user, $cookieToken);
            }

            return response()->json([
                'success' => true,
                'message' => 'Login successful',
                'redirect_url' => route('home')
            ]);

        } catch (\RuntimeException $e) {
            return response()->json([
                'success' => false,
                'errors' => ['password' => ['Decryption failed. Please try again.']]
            ], 500);
        } catch (\Illuminate\Validation\ValidationException $e) {
            return response()->json([
                'success' => false,
                'errors' => $e->errors()
            ], 422);
        }
    }

    /**
     * API: Request OTP (JSON response)
     */
    public function requestOtpApi(Request $request): JsonResponse
    {
        try {
            $request->validate([
                'mobile' => ['required', 'string', 'regex:/^[6-9]\d{9}$/'],
            ]);

            $mobile = preg_replace('/^\+91/', '', $request->input('mobile'));
            $normalizedMobile = preg_replace('/^\+91/', '', $mobile);

            $otp = $this->otpService->generateOtp();
            $sent = $this->otpService->sendLoginOtp($normalizedMobile, $otp);

            if (!$sent) {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to send OTP. Please try again.'
                ], 500);
            }

            $this->otpService->storeOtp($normalizedMobile, $otp);

            return response()->json([
                'success' => true,
                'message' => 'OTP sent to your mobile number.',
                'mobile' => $normalizedMobile
            ]);

        } catch (\Illuminate\Validation\ValidationException $e) {
            return response()->json([
                'success' => false,
                'errors' => $e->errors()
            ], 422);
        }
    }

    /**
     * API: Verify OTP (JSON response)
     */
    public function verifyOtpApi(Request $request): JsonResponse
    {
        try {
            $request->validate([
                'mobile' => ['required', 'string'],
                'otp' => ['required', 'string', 'regex:/^\d{6}$/'],
            ]);

            $normalizedMobile = preg_replace('/^\+91/', '', $request->input('mobile'));
            $verified = $this->otpService->verifyOtp($normalizedMobile, $request->input('otp'));

            if (!$verified) {
                return response()->json([
                    'success' => false,
                    'errors' => ['otp' => ['Invalid or expired OTP.']]
                ], 422);
            }

            $user = User::firstOrCreate(
                ['mobile' => $normalizedMobile],
                ['name' => $normalizedMobile]
            );

            $user->update(['last_login_at' => now()]);
            auth()->login($user, true);

            // Merge guest cart
            $cookieToken = $request->cookie('cart_token');
            if ($cookieToken) {
                $this->cartService->mergeGuestCartWithUser($user, $cookieToken);
            }

            return response()->json([
                'success' => true,
                'message' => 'Login successful',
                'redirect_url' => route('home')
            ]);

        } catch (\Illuminate\Validation\ValidationException $e) {
            return response()->json([
                'success' => false,
                'errors' => $e->errors()
            ], 422);
        }
    }
}
