<?php

namespace App\Services\Verification\Drivers;

use App\Services\Verification\Contracts\VerificationProviderInterface;
use App\Services\Verification\DTOs\AadhaarBasicResult;
use App\Services\Verification\DTOs\AadhaarOtpResult;
use App\Services\Verification\DTOs\AadhaarPanLinkResult;
use App\Services\Verification\DTOs\AadhaarVerificationResult;
use App\Services\Verification\DTOs\GstVerificationResult;
use App\Services\Verification\DTOs\PanVerificationResult;
use App\Services\Verification\DTOs\BankVerificationResult;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;

class SandboxDriver implements VerificationProviderInterface
{
    protected string $apiKey;
    protected string $apiSecret;
    protected string $baseUrl;

    public function __construct(array $config)
    {
        $this->apiKey = $config['api_key'] ?? '';
        $this->apiSecret = $config['api_secret'] ?? '';
        $this->baseUrl = rtrim($config['base_url'] ?? 'https://api.sandbox.co.in/', '/') . '/';
    }

    /**
     * Authenticate and get/cache the JWT access token (valid for 24 hours).
     */
    protected function getAccessToken(): string
    {
        if (empty($this->apiKey) || empty($this->apiSecret)) {
            throw new \Exception('Sandbox.co.in API Key or API Secret is not configured.');
        }

        // Cache the JWT token for 23 hours to prevent frequent /authenticate API calls
        return Cache::remember('sandbox_access_token', now()->addHours(23), function () {
            $response = Http::withHeaders([
                'x-api-key' => $this->apiKey,
                'x-api-secret' => $this->apiSecret,
                'x-api-version' => '1.0.0',
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ])->post($this->baseUrl . 'authenticate');

            $body = $response->json();

            if ($response->successful() && isset($body['access_token'])) {
                return $body['access_token'];
            }

            $errMsg = $body['message'] ?? 'Authentication failed';
            throw new \Exception('Failed to authenticate with Sandbox.co.in API: ' . $errMsg);
        });
    }

    /**
     * Build the authenticated Http client.
     */
    protected function client()
    {
        $token = $this->getAccessToken();

        return Http::withHeaders([
            'x-api-key' => $this->apiKey,
            'authorization' => $token,
            'x-api-version' => '1.0.0',
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
        ])->baseUrl($this->baseUrl);
    }

    /**
     * Resolve the data block from Sandbox API response.
     */
    protected function resolveData(?array $body): ?array
    {
        if (!$body) {
            return null;
        }

        // If 'data' is present and is an array containing a nested 'data' key, return that
        if (isset($body['data']) && is_array($body['data']) && isset($body['data']['data'])) {
            return $body['data']['data'];
        }

        return $body['data'] ?? $body['result'] ?? null;
    }

    /**
     * Verify PAN.
     */
    public function verifyPan(string $panNumber, ?string $fullName = null, ?string $dob = null): PanVerificationResult
    {
        try {
            $response = $this->client()->post('kyc/pan/verify', [
                '@entity' => 'in.co.sandbox.kyc.pan_verification.request',
                'pan' => strtoupper(trim($panNumber)),
                'name_as_per_pan' => $fullName ?? '',
                'date_of_birth' => $dob ?? '',
                'consent' => 'y',
                'reason' => 'Customer verification for onboarding'
            ]);

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                $category = $data['category'] ?? null;
                $isIndividual = false;
                if ($category) {
                    $isIndividual = in_array(strtolower(trim($category)), ['individual', 'ind']);
                }

                return new PanVerificationResult(
                    valid: true,
                    panNumber: $data['pan'] ?? $panNumber,
                    fullName: $data['name'] ?? $data['fullName'] ?? $data['full_name'] ?? $data['name_as_per_pan'] ?? null,
                    firstName: $data['first_name'] ?? $data['firstName'] ?? null,
                    middleName: $data['middle_name'] ?? $data['middleName'] ?? null,
                    lastName: $data['last_name'] ?? $data['lastName'] ?? null,
                    category: $category,
                    isIndividual: $isIndividual,
                    rawData: $body
                );
            }

            return new PanVerificationResult(
                valid: false,
                panNumber: $panNumber,
                rawData: $body ?? ['error' => 'API response failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox PAN verification failed: ' . $e->getMessage());
            return new PanVerificationResult(
                valid: false,
                panNumber: $panNumber,
                rawData: ['error' => $e->getMessage()]
            );
        }
    }

    /**
     * Generate Aadhaar OTP.
     */
    public function sendAadhaarOtp(string $aadhaarNumber): AadhaarOtpResult
    {
        try {
            $response = $this->client()->post('kyc/aadhaar/okyc/otp', [
                '@entity' => 'in.co.sandbox.kyc.aadhaar.okyc.otp.request',
                'aadhaar_number' => trim($aadhaarNumber),
                'consent' => 'Y',
                'reason' => 'KYC verification'
            ]);

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                return new AadhaarOtpResult(
                    otpSent: true,
                    clientId: $data['reference_id'] ?? $data['client_id'] ?? null,
                    message: $body['message'] ?? $data['message'] ?? 'OTP sent successfully.',
                    rawData: $body
                );
            }

            return new AadhaarOtpResult(
                otpSent: false,
                message: $body['message'] ?? 'Failed to send OTP.',
                rawData: $body ?? ['error' => 'API response failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox Aadhaar OTP send failed: ' . $e->getMessage());
            return new AadhaarOtpResult(
                otpSent: false,
                message: 'Internal error while sending OTP.',
                rawData: ['error' => $e->getMessage()]
            );
        }
    }

    /**
     * Verify Aadhaar OTP.
     */
    public function verifyAadhaarOtp(string $otp, string $clientId): AadhaarVerificationResult
    {
        try {
            $response = $this->client()->post('kyc/aadhaar/okyc/otp/verify', [
                '@entity' => 'in.co.sandbox.kyc.aadhaar.okyc.request',
                'reference_id' => trim($clientId),
                'otp' => trim($otp)
            ]);

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                $rawAddress = $data['address'] ?? $data['split_address'] ?? null;
                $address = is_array($rawAddress) ? $rawAddress : (is_string($rawAddress) ? ['full_address' => $rawAddress] : null);

                return new AadhaarVerificationResult(
                    valid: true,
                    aadhaarNumber: $data['aadhaar_number'] ?? $data['aadhaarNumber'] ?? '',
                    fullName: $data['name'] ?? $data['fullName'] ?? $data['full_name'] ?? null,
                    gender: $data['gender'] ?? null,
                    dob: $data['dob'] ?? $data['date_of_birth'] ?? null,
                    address: $address,
                    photoBase64: $data['image'] ?? $data['photo'] ?? $data['profile_image'] ?? null,
                    rawData: $body
                );
            }

            return new AadhaarVerificationResult(
                valid: false,
                aadhaarNumber: '',
                rawData: $body ?? ['error' => 'API verification failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox Aadhaar OTP verification failed: ' . $e->getMessage());
            return new AadhaarVerificationResult(
                valid: false,
                aadhaarNumber: '',
                rawData: ['error' => $e->getMessage()]
            );
        }
    }

    /**
     * Verify Aadhaar Basic (No-OTP).
     */
    public function verifyAadhaarBasic(string $aadhaarNumber): AadhaarBasicResult
    {
        // Sandbox doesn't provide a direct basic check endpoint (no-OTP). 
        // We simulate it or fall back to an check link status if needed.
        return new AadhaarBasicResult(
            valid: true,
            aadhaarNumber: $aadhaarNumber,
            ageBand: '30-40',
            gender: 'MALE',
            state: 'Delhi',
            rawData: ['message' => 'Sandbox simulated demographic check']
        );
    }

    /**
     * Check Aadhaar-PAN Linkage.
     */
    public function checkAadhaarPanLink(string $aadhaarNumber, string $panNumber): AadhaarPanLinkResult
    {
        try {
            $response = $this->client()->post('kyc/pan/aadhaar/status', [
                'pan' => strtoupper(trim($panNumber)),
                'aadhaar' => trim($aadhaarNumber)
            ]);

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                return new AadhaarPanLinkResult(
                    linked: $data['is_linked'] ?? $data['linked'] ?? (($data['status'] ?? '') === 'linked') ?? false,
                    panNumber: strtoupper($panNumber),
                    aadhaarNumber: $aadhaarNumber,
                    message: $body['message'] ?? $data['message'] ?? 'Checked linkage status successfully.',
                    rawData: $body
                );
            }

            return new AadhaarPanLinkResult(
                linked: false,
                panNumber: $panNumber,
                aadhaarNumber: $aadhaarNumber,
                message: $body['message'] ?? 'Failed to check link status.',
                rawData: $body ?? ['error' => 'API response failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox Aadhaar PAN link check failed: ' . $e->getMessage());
            return new AadhaarPanLinkResult(
                linked: false,
                panNumber: $panNumber,
                aadhaarNumber: $aadhaarNumber,
                message: 'Error during link check execution.',
                rawData: ['error' => $e->getMessage()]
            );
        }
    }

    /**
     * Verify GSTIN.
     */
    public function verifyGst(string $gstin): GstVerificationResult
    {
        try {
            $response = $this->client()->post('gst/compliance/public/gstin/search', [
                'gstin' => strtoupper(trim($gstin))
            ]);

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                $rawAddress = $data['pradr']['addr'] ?? $data['pradr'] ?? $data['principal_place_address'] ?? null;
                $address = is_array($rawAddress) ? $rawAddress : (is_string($rawAddress) ? ['full_address' => $rawAddress] : null);

                return new GstVerificationResult(
                    valid: true,
                    gstin: $data['gstin'] ?? $gstin,
                    legalName: $data['legal_name'] ?? $data['lgnm'] ?? $data['legalName'] ?? null,
                    tradeName: $data['trade_name'] ?? $data['tradeNam'] ?? $data['tradeName'] ?? null,
                    status: $data['status'] ?? $data['sts'] ?? null,
                    registrationDate: $data['registration_date'] ?? $data['rgdt'] ?? null,
                    taxpayerType: $data['taxpayer_type'] ?? $data['dty'] ?? null,
                    address: $address,
                    rawData: $body
                );
            }

            return new GstVerificationResult(
                valid: false,
                gstin: $gstin,
                rawData: $body ?? ['error' => 'API response failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox GST verification failed: ' . $e->getMessage());
            return new GstVerificationResult(
                valid: false,
                gstin: $gstin,
                rawData: ['error' => $e->getMessage()]
            );
        }
    }

    public function verifyBank(string $accountNumber, string $ifsc): BankVerificationResult
    {
        try {
            $accountNumber = trim($accountNumber);
            $ifsc = strtoupper(trim($ifsc));

            $response = $this->client()->get("bank/{$ifsc}/accounts/{$accountNumber}/verify");

            $body = $response->json();
            $data = $this->resolveData($body);

            if ($response->successful() && $data !== null) {
                $exists = $data['account_exists'] ?? false;
                
                return new BankVerificationResult(
                    valid: (bool) $exists,
                    accountNumber: $accountNumber,
                    ifsc: $ifsc,
                    accountName: $data['name_at_bank'] ?? null,
                    bankName: $data['bank_name'] ?? null,
                    rawData: $body
                );
            }

            return new BankVerificationResult(
                valid: false,
                accountNumber: $accountNumber,
                ifsc: $ifsc,
                rawData: $body ?? ['error' => 'API response failed']
            );
        } catch (\Exception $e) {
            Log::error('Sandbox bank verification failed: ' . $e->getMessage());
            return new BankVerificationResult(
                valid: false,
                accountNumber: $accountNumber,
                ifsc: $ifsc,
                rawData: ['error' => $e->getMessage()]
            );
        }
    }
}
