<?php

namespace App\Services;

use App\Models\IndividualRegistration;
use App\Models\PortalUser;
use App\Notifications\IndividualRegistrationReceivedNotification;
use App\Notifications\IndividualRegistrationRejectedNotification;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;

class IndividualRegistrationService
{
    public function __construct(
        private readonly RegistrationFileUploadService $fileUploadService,
        private readonly NinEncryptionService $ninEncryptionService,
        private readonly PortalUserService $portalUserService
    ) {}

    /**
     * Submit a new individual registration from the public endpoint.
     */
    public function submitPublicRegistrationForm(array $data): IndividualRegistration
    {
        try {
            return DB::transaction(function () use ($data) {
                // Upload files
                $filePaths = $this->uploadKycFiles($data);

                // Prepare identity card number fields based on type
                $identityCardFields = $this->prepareIdentityCardNumberFields($data);

                // Create individual registration
                $submission = IndividualRegistration::create([
                    'submission_type' => 'public_form',
                    'status' => IndividualRegistration::STATUS_PENDING,
                    // BIO-DATA INFORMATION
                    'title' => $data['title'] ?? null,
                    'first_name' => $data['first_name'],
                    'last_name' => $data['last_name'],
                    'middle_name' => $data['middle_name'] ?? null,
                    'gender' => $data['gender'] ?? null,
                    'date_of_birth' => $data['date_of_birth'],
                    'nationality' => $data['nationality'],
                    'phone_number' => $data['phone_number'],
                    'confirm_phone_number' => $data['confirm_phone_number'],
                    'foreign_number' => $data['foreign_number'] ?? null,
                    'email_address' => $data['email_address'],
                    'confirm_email_address' => $data['confirm_email_address'],
                    'country' => $data['country'],
                    'state' => $data['state'],
                    'city' => $data['city'],
                    'chn' => $data['chn'] ?? null,
                    'house_address' => $data['house_address'],
                    'previous_address' => $data['previous_address'] ?? null,
                    // BANK INFORMATION
                    // Decrypt RSA-encrypted BVN, re-encrypt with AES-256-GCM, store packed
                    'bvn_encrypted' => $this->reEncryptWithAes($data['encrypted_bvn']),
                    'bank_name' => $data['bank_name'] ?? null,
                    'bank_account_number' => $data['bank_account_number'] ?? null,
                    'tin_or_pin' => $data['tin_or_pin'] ?? null,
                    'bank_details_consent' => $data['bank_details_consent'] ?? false,
                    // IDENTITY INFORMATION
                    'identity_card_type' => $data['identity_card_type'],
                    ...$identityCardFields,
                    // File paths
                    'identity_card_path' => $filePaths['identity_card'] ?? null,
                    'scanned_signature_path' => $filePaths['scanned_signature'] ?? null,
                    'passport_photograph_path' => $filePaths['passport_photograph'] ?? null,
                    'nin_document_path' => $filePaths['nin_document'] ?? null,
                    // Consent
                    'consent_given' => true,
                ]);

                // Send confirmation email
                try {
                    Notification::route('mail', $submission->email_address)
                        ->notify(new IndividualRegistrationReceivedNotification($submission));
                } catch (\Exception $e) {
                    Log::error('Failed to send individual registration confirmation email: ' . $e->getMessage(), [
                        'submission_id' => $submission->id,
                        'exception' => $e,
                    ]);
                }

                return $submission;
            });
        } catch (\Exception $e) {
            Log::error('Error submitting public individual registration: ' . $e->getMessage(), [
                'exception' => $e,
                'data' => $data,
            ]);
            throw $e;
        }
    }

    /**
     * Approve an individual registration.
     * Sets status to approved but does NOT create portal account or send invitation yet.
     */
    public function approveSubmission(IndividualRegistration $submission, int $approvedBy): IndividualRegistration
    {
        try {
            return DB::transaction(function () use ($submission, $approvedBy) {
                $submission->update([
                    'status' => IndividualRegistration::STATUS_APPROVED,
                    'approved_by' => $approvedBy,
                    'approved_at' => now(),
                    'account_status' => IndividualRegistration::ACCOUNT_STATUS_PENDING,
                ]);

                Log::info('Individual registration approved', [
                    'submission_id' => $submission->id,
                    'approved_by' => $approvedBy,
                ]);

                return $submission->fresh();
            });
        } catch (\Exception $e) {
            Log::error('Error approving individual registration: ' . $e->getMessage(), [
                'submission_id' => $submission->id,
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    /**
     * Send invitation to an individual registrant.
     * Creates a PortalUser with role=individual and sends the invitation email.
     * If already invited, resends to the existing PortalUser.
     */
    public function sendInvitation(IndividualRegistration $submission, ?string $email = null, ?int $approvedBy = null): PortalUser
    {
        try {
            if ($submission->isRejected()) {
                throw new \Exception('Cannot send invitation to a rejected registration.');
            }

            if ($submission->isAccountActive()) {
                throw new \Exception('Account is already active. Cannot resend invitation.');
            }

            return DB::transaction(function () use ($submission, $email, $approvedBy) {
                // Approve the registration if not already approved
                if ($submission->isPending()) {
                    $submission->update([
                        'status' => IndividualRegistration::STATUS_APPROVED,
                        'approved_by' => $approvedBy,
                        'approved_at' => now(),
                    ]);
                    $submission->refresh();
                }

                // Resend: account already invited and a portal user already exists
                if ($submission->isAccountInvited()) {
                    $portalUser = PortalUser::where('individual_registration_id', $submission->id)
                        ->where('role', 'individual')
                        ->first();

                    if (! $portalUser) {
                        throw new \Exception('Portal user not found for resending invitation.');
                    }

                    if ($portalUser->hasSetPassword()) {
                        throw new \Exception('Password already set. Account is active.');
                    }

                    $this->portalUserService->sendInvitation($portalUser, $email);

                    Log::info('Individual registration invitation resent', [
                        'submission_id' => $submission->id,
                        'portal_user_id' => $portalUser->id,
                    ]);

                    return $portalUser->fresh();
                }

                // First time — create PortalUser with role=individual
                $portalUser = PortalUser::create([
                    'individual_registration_id' => $submission->id,
                    'name' => $submission->first_name . ' ' . $submission->last_name,
                    'email' => $submission->email_address,
                    'role' => 'individual',
                ]);

                // Send invitation
                $this->portalUserService->sendInvitation($portalUser, $email);

                // Update account status to invited
                $submission->update(['account_status' => IndividualRegistration::ACCOUNT_STATUS_INVITED]);

                Log::info('Individual registration invitation sent', [
                    'submission_id' => $submission->id,
                    'portal_user_id' => $portalUser->id,
                    'approved_by' => $approvedBy,
                ]);

                return $portalUser->fresh();
            });
        } catch (\Exception $e) {
            Log::error('Error sending individual registration invitation: ' . $e->getMessage(), [
                'submission_id' => $submission->id,
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    /**
     * Verify an individual registration (mark as verified).
     */
    public function verifyRegistration(IndividualRegistration $submission, int $verifiedBy): IndividualRegistration
    {
        try {
            return DB::transaction(function () use ($submission, $verifiedBy) {
                if (! $submission->isApproved()) {
                    throw new \Exception('Only approved registrations can be verified.');
                }

                $submission->update([
                    'status' => IndividualRegistration::STATUS_VERIFIED,
                ]);

                Log::info('Individual registration verified', [
                    'submission_id' => $submission->id,
                    'verified_by' => $verifiedBy,
                ]);

                return $submission->fresh();
            });
        } catch (\Exception $e) {
            Log::error('Error verifying individual registration: ' . $e->getMessage(), [
                'submission_id' => $submission->id,
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    /**
     * Reject an individual registration.
     */
    public function rejectSubmission(IndividualRegistration $submission, int $rejectedBy, ?string $reason = null): IndividualRegistration
    {
        try {
            return DB::transaction(function () use ($submission, $rejectedBy, $reason) {
                $submission->update([
                    'status' => IndividualRegistration::STATUS_REJECTED,
                    'rejection_reason' => $reason,
                    'rejected_by' => $rejectedBy,
                    'rejected_at' => now(),
                ]);

                try {
                    Notification::route('mail', $submission->email_address)
                        ->notify(new IndividualRegistrationRejectedNotification($submission));
                } catch (\Exception $e) {
                    Log::error('Failed to send individual registration rejection email: ' . $e->getMessage(), [
                        'submission_id' => $submission->id,
                        'exception' => $e,
                    ]);
                }

                Log::info('Individual registration rejected', [
                    'submission_id' => $submission->id,
                    'rejected_by' => $rejectedBy,
                    'reason' => $reason,
                ]);

                return $submission->fresh();
            });
        } catch (\Exception $e) {
            Log::error('Error rejecting individual registration: ' . $e->getMessage(), [
                'submission_id' => $submission->id,
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    /**
     * Get all pending individual registrations.
     */
    public function getPendingSubmissions(int $perPage = 15)
    {
        return IndividualRegistration::pending()
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);
    }

    /**
     * Get all individual registrations with optional filters.
     */
    public function getAllSubmissions(array $filters = [], int $perPage = 15)
    {
        $query = IndividualRegistration::query();

        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (isset($filters['submission_type'])) {
            $query->where('submission_type', $filters['submission_type']);
        }

        if (isset($filters['account_status'])) {
            $query->where('account_status', $filters['account_status']);
        }

        if (isset($filters['from_date'])) {
            $query->whereDate('created_at', '>=', $filters['from_date']);
        }

        if (isset($filters['to_date'])) {
            $query->whereDate('created_at', '<=', $filters['to_date']);
        }

        return $query->orderBy('created_at', 'desc')->paginate($perPage);
    }

    /**
     * Get all individual registrations as a lazy collection for export.
     */
    public function getSubmissionsForExport(array $filters = [])
    {
        $query = IndividualRegistration::query();

        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }
        if (isset($filters['submission_type'])) {
            $query->where('submission_type', $filters['submission_type']);
        }
        if (isset($filters['account_status'])) {
            $query->where('account_status', $filters['account_status']);
        }
        if (isset($filters['from_date'])) {
            $query->whereDate('created_at', '>=', $filters['from_date']);
        }
        if (isset($filters['to_date'])) {
            $query->whereDate('created_at', '<=', $filters['to_date']);
        }

        return $query->orderBy('created_at', 'desc')->cursor();
    }

    /**
     * Get a specific submission with relationships.
     */
    public function getSubmission(IndividualRegistration $submission): IndividualRegistration
    {
        return $submission->load(['approvedBy', 'rejectedBy']);
    }

    /**
     * Update an individual registration.
     */
    public function updateSubmission(IndividualRegistration $submission, array $data): IndividualRegistration
    {
        try {
            return DB::transaction(function () use ($submission, $data) {
                if ($submission->isRejected()) {
                    throw new \Exception('Cannot update a rejected submission.');
                }

                $submission->update($data);

                Log::info('Individual registration updated', [
                    'submission_id' => $submission->id,
                    'updated_fields' => array_keys($data),
                ]);

                return $submission->fresh();
            });
        } catch (\Exception $e) {
            Log::error('Error updating individual registration: ' . $e->getMessage(), [
                'submission_id' => $submission->id,
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    /**
     * Upload all KYC files.
     */
    private function uploadKycFiles(array $data): array
    {
        $filePaths = [];

        if (isset($data['identity_card'])) {
            $filePaths['identity_card'] = $this->fileUploadService->uploadDocument(
                $data['identity_card'],
                'identity_card'
            );
        }

        if (isset($data['scanned_signature'])) {
            $filePaths['scanned_signature'] = $this->fileUploadService->uploadDocument(
                $data['scanned_signature'],
                'scanned_signature'
            );
        }

        if (isset($data['passport_photograph'])) {
            $filePaths['passport_photograph'] = $this->fileUploadService->uploadDocument(
                $data['passport_photograph'],
                'passport_photograph'
            );
        }

        if (isset($data['nin_document'])) {
            $filePaths['nin_document'] = $this->fileUploadService->uploadDocument(
                $data['nin_document'],
                'nin_document'
            );
        }

        return $filePaths;
    }

    /**
     * Prepare identity card number fields based on card type.
     */
    private function prepareIdentityCardNumberFields(array $data): array
    {
        if ($data['identity_card_type'] === 'National Identity Number') {
            return [
                'identity_card_number' => null,
                'nin_number_encrypted' => $this->reEncryptWithAes($data['encrypted_nin']),
            ];
        }

        return [
            'identity_card_number' => $data['identity_card_number'] ?? null,
            'nin_number_encrypted' => null,
        ];
    }

    /**
     * RSA-decrypt a frontend-encrypted value, re-encrypt with AES-256-GCM,
     * and return the packed "iv:ciphertext:tag" string for storage.
     * In the testing environment, returns a placeholder packed string.
     */
    private function reEncryptWithAes(string $rsaEncrypted): string
    {
        if (app()->environment('testing')) {
            return 'dGVzdA==:dGVzdA==:dGVzdA==';
        }

        $plaintext = $this->ninEncryptionService->decryptWithPrivateKey($rsaEncrypted);
        $aes = $this->ninEncryptionService->encrypt($plaintext);

        return $aes['iv'] . ':' . $aes['ciphertext'] . ':' . $aes['tag'];
    }
}
