<?php

namespace App\Services;

use App\Models\User;
use App\Models\UserProfile;
use App\Models\TrainingPackage;
use App\Models\TrainingRegistration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Spatie\Permission\Models\Role;

/**
 * RegistrationService
 *
 * Handles user registration, contributor enrollment, training registration,
 * payment processing, and enrollment management.
 *
 * @package App\Services
 */
class RegistrationService
{
    protected PaymentService $paymentService;
    protected NotificationService $notificationService;

    public function __construct(
        PaymentService $paymentService,
        NotificationService $notificationService
    ) {
        $this->paymentService = $paymentService;
        $this->notificationService = $notificationService;
    }

    // -------------------------------------------------------------------------
    // General User Registration
    // -------------------------------------------------------------------------

    /**
     * Register a new user with the given validated data.
     */
    public function register(array $data): User
    {
        return DB::transaction(function () use ($data) {
            $user = User::create([
                'name'              => strip_tags($data['name']),
                'email'             => $data['email'],
                'username'          => !empty($data['username']) ? $data['username'] : User::generateUsername($data['email']),
                'password'          => Hash::make($data['password']),
                'is_active'         => true,
                'email_verified_at' => null,
            ]);

            if (!empty($data['role'])) {
                $role = Role::where('name', $data['role'])->first();
                if ($role) {
                    $user->assignRole($role);
                }
            }

            return $user;
        });
    }

    /**
     * Create a user from social OAuth data.
     * Used by SocialAuthController when a new social user completes registration.
     */
    public function createFromSocial(array $socialData, string $provider): User
    {
        return DB::transaction(function () use ($socialData) {
            $email  = $socialData['email'];
            $name   = $socialData['name'] ?? $socialData['nickname'] ?? 'User';
            $avatar = $socialData['avatar'] ?? null;

            // Re-check in case the user was created between the callback and this call
            $existing = User::where('email', $email)->first();
            if ($existing) {
                return $existing;
            }

            // Build the user without password (social-only account)
            // We use forceFill + save to bypass the 'hashed' cast on password
            // so the column stays NULL rather than a hash of an empty string.
            $user = new User();
            $user->forceFill([
                'name'              => strip_tags($name),
                'email'             => $email,
                'username'          => User::generateUsername($email),
                'password'          => null,
                'avatar'            => $avatar,
                'is_active'         => true,
                'email_verified_at' => now(),
            ]);
            $user->save();

            return $user;
        });
    }

    /**
     * Get available roles for registration.
     * Excludes privileged roles that cannot be self-assigned.
     */
    public function getAvailableRoles()
    {
        return Role::whereNotIn('name', ['Super Admin', 'Admin', 'Super Manager', 'Manager'])->get();
    }

    /**
     * Get role-specific registration fields.
     */
    public function getRoleSpecificFields(string $role): array
    {
        // Extend as needed per role
        return [];
    }

    /**
     * Check if a username is available.
     */
    public function isUsernameAvailable(string $username): bool
    {
        return !User::where('username', $username)->exists();
    }

    /**
     * Check if an email is available.
     */
    public function isEmailAvailable(string $email): bool
    {
        return !User::where('email', $email)->exists();
    }

    /**
     * Check if an email is already registered.
     */
    public function isEmailRegistered(string $email): bool
    {
        return User::where('email', $email)->exists();
    }

    // -------------------------------------------------------------------------
    // Contributor Registration
    // -------------------------------------------------------------------------

    /**
     * Register a new contributor.
     *
     * Supports both the legacy single `name` field and the newer
     * `first_name` / `last_name` split introduced in the redesigned form.
     * Creates a UserProfile with any optional profile fields supplied.
     */
    public function registerContributor(array $data): User
    {
        return DB::transaction(function () use ($data) {
            // Build the display name from first/last or fall back to legacy `name`
            if (!empty($data['first_name']) || !empty($data['last_name'])) {
                $name = trim(
                    strip_tags($data['first_name'] ?? '') . ' ' .
                    strip_tags($data['last_name'] ?? '')
                );
            } else {
                $name = strip_tags($data['name'] ?? '');
            }

            $user = User::create([
                'name'              => $name,
                'email'             => $data['email'],
                'username'          => User::generateUsername($data['email']),
                'password'          => Hash::make($data['password']),
                'phone'             => $data['phone'] ?? null,
                'is_active'         => true,
                'email_verified_at' => now(),
            ]);

            $this->assignContributorRole($user);

            // Create a UserProfile with any optional fields provided
            UserProfile::create([
                'user_id'           => $user->id,
                'bio'               => $data['bio'] ?? null,
                'city'              => $data['city'] ?? null,
                'country'           => $data['country'] ?? null,
                'website'           => $data['portfolio_url'] ?? null,
                'contribution_type' => $data['contribution_type'] ?? null,
                'role_specific_data' => array_filter([
                    'occupation'        => $data['occupation'] ?? null,
                    'organization'      => $data['organization'] ?? null,
                    'contribution_type' => $data['contribution_type'] ?? null,
                ]),
            ]);

            Log::info('Contributor registered', ['user_id' => $user->id]);

            return $user;
        });
    }

    /**
     * Assign the Contributor role to a user.
     */
    public function assignContributorRole(User $user): void
    {
        $role = Role::firstOrCreate(
            ['name' => 'Contributor'],
            ['guard_name' => 'web']
        );
        $user->assignRole($role);
    }

    /**
     * Ensure user has Contributor role.
     */
    public function ensureContributorRole(User $user): void
    {
        if (!$user->hasRole('Contributor')) {
            $this->assignContributorRole($user);
        }
    }

    // -------------------------------------------------------------------------
    // Training Registration
    // -------------------------------------------------------------------------

    /**
     * Register a user for a training package.
     */
    public function registerUser(User $user, TrainingPackage $package, array $additionalData = []): array
    {
        return DB::transaction(function () use ($user, $package, $additionalData) {
            $existing = TrainingRegistration::where('user_id', $user->id)
                ->where('training_package_id', $package->id)
                ->first();

            if ($existing) {
                return [
                    'success'      => false,
                    'message'      => 'You are already registered for this training.',
                    'registration' => $existing,
                ];
            }

            if (!$package->hasCapacity()) {
                return [
                    'success'  => false,
                    'message'  => 'This training is fully booked. No more slots available.',
                ];
            }

            $initialStatus = $package->requires_approval ? 'pending' : 'approved';
            $paymentStatus = $package->requiresPayment() ? 'pending' : 'not_required';

            $registration = TrainingRegistration::create([
                'user_id'             => $user->id,
                'training_package_id' => $package->id,
                'registration_date'   => now(),
                'status'              => $initialStatus,
                'payment_status'      => $paymentStatus,
                'amount_due'          => $package->requiresPayment() ? $package->fee_amount : 0.00,
                'registration_source' => $additionalData['source'] ?? 'dashboard',
                'registered_by'       => auth()->id() ?? $user->id,
                'notes'               => $additionalData['notes'] ?? null,
                'metadata'            => $additionalData['metadata'] ?? null,
            ]);

            Log::info('Training registration created', [
                'registration_id' => $registration->id,
                'user_id'         => $user->id,
                'package_id'      => $package->id,
                'status'          => $registration->status,
            ]);

            if ($initialStatus === 'approved' && $paymentStatus === 'not_required') {
                $this->confirmEnrollment($registration);
            }

            $this->notificationService->sendRegistrationConfirmation($registration);

            return [
                'success'          => true,
                'message'          => 'Registration successful!',
                'registration'     => $registration,
                'requires_payment' => $package->requiresPayment(),
                'requires_approval'=> $package->requires_approval,
            ];
        });
    }

    /**
     * Process payment for a registration.
     */
    public function processPayment(
        TrainingRegistration $registration,
        string $phoneNumber,
        string $paymentMethod = 'mobile',
        ?string $cardNumber = null,
        ?string $cardExpiry = null,
        ?string $cardCvv = null
    ): array {
        $package = $registration->trainingPackage;

        if (!$package->requiresPayment()) {
            return ['success' => true, 'message' => 'No payment required for this training.'];
        }

        if (!$this->paymentService->canCollect()) {
            return ['success' => false, 'message' => 'Payment service is not available. Please contact support.'];
        }

        try {
            // Use TR-REG- prefix so MomoCallbackController can route callbacks correctly
            $reference    = 'TR-REG-' . $registration->id . '-' . time();
            $storedMethod = $paymentMethod === 'card' ? 'mtn_momo_card' : 'mtn_momo_mobile';

            if ($paymentMethod === 'card') {
                $result = $this->paymentService->initiateCardCollection(
                    cardNumber: $cardNumber ?? '',
                    expiry: $cardExpiry ?? '',
                    cvv: $cardCvv ?? '',
                    amount: $package->fee_amount,
                    reference: $reference,
                    payerMessage: 'Payment for ' . $package->name,
                    payeeNote: 'Training fee payment'
                );
            } else {
                $result = $this->paymentService->initiateCollection(
                    phoneNumber: $phoneNumber,
                    amount: $package->fee_amount,
                    reference: $reference,
                    payerMessage: 'Payment for ' . $package->name,
                    payeeNote: 'Training fee payment'
                );
            }

            if ($result['success']) {
                $registration->update([
                    'payment_status'    => 'processing',
                    'payment_reference' => $reference,
                    'payment_method'    => $storedMethod,
                    'metadata'          => array_merge($registration->metadata ?? [], [
                        'payment_phone'        => $phoneNumber,
                        'payment_initiated_at' => now()->toIso8601String(),
                    ]),
                ]);

                return [
                    'success'   => true,
                    'message'   => 'Payment request sent! Please check your phone to complete payment.',
                    'reference' => $reference,
                ];
            }

            return ['success' => false, 'message' => $result['message'] ?? 'Failed to initiate payment. Please try again.'];

        } catch (\Exception $e) {
            Log::error('Payment processing failed', ['registration_id' => $registration->id, 'error' => $e->getMessage()]);
            return ['success' => false, 'message' => 'An error occurred while processing payment. Please try again.'];
        }
    }

    /**
     * Handle payment callback/webhook from MomoCallbackController.
     *
     * Called when MTN MoMo posts a callback with a TR-REG- prefixed externalId.
     */
    public function handlePaymentCallback(string $reference, string $status, array $additionalData = []): bool
    {
        return DB::transaction(function () use ($reference, $status, $additionalData) {
            $registration = TrainingRegistration::where('payment_reference', $reference)->first();

            if (!$registration) {
                Log::warning('Payment callback for unknown training registration', ['reference' => $reference]);
                return false;
            }

            if ($status === 'SUCCESSFUL') {
                $registration->updatePaymentStatus('completed', $reference, $registration->trainingPackage->fee_amount);

                if ($registration->status === 'approved' || !$registration->trainingPackage->requires_approval) {
                    $this->confirmEnrollment($registration);
                }

                // Send payment confirmation — wrapped so it never blocks activation
                try {
                    $this->notificationService->sendPaymentConfirmation($registration);
                } catch (\Throwable $e) {
                    Log::error('Failed to send training payment confirmation notification', [
                        'registration_id' => $registration->id,
                        'error'           => $e->getMessage(),
                    ]);
                }

            } elseif (in_array($status, ['FAILED', 'REJECTED'])) {
                $registration->updatePaymentStatus('failed');

                // Send failure notification — wrapped so it never blocks status update
                try {
                    $this->notificationService->sendTrainingPaymentFailed(
                        $registration,
                        $additionalData['reason'] ?? 'Payment was not completed.'
                    );
                } catch (\Throwable $e) {
                    Log::error('Failed to send training payment failure notification', [
                        'registration_id' => $registration->id,
                        'error'           => $e->getMessage(),
                    ]);
                }
            }

            return true;
        });
    }

    /**
     * Approve a registration.
     */
    public function approveRegistration(TrainingRegistration $registration, int $approverId, ?string $notes = null): bool
    {
        return DB::transaction(function () use ($registration, $approverId, $notes) {
            $registration->approve($approverId);

            if ($notes) {
                $registration->update(['notes' => $notes]);
            }

            if (in_array($registration->payment_status, ['completed', 'not_required'])) {
                $this->confirmEnrollment($registration);
            }

            $this->notificationService->sendApprovalNotification($registration);

            Log::info('Registration approved', ['registration_id' => $registration->id, 'approver_id' => $approverId]);

            return true;
        });
    }

    /**
     * Reject a registration.
     */
    public function rejectRegistration(TrainingRegistration $registration, int $rejectorId, string $reason): bool
    {
        return DB::transaction(function () use ($registration, $rejectorId, $reason) {
            $registration->reject($rejectorId, $reason);

            if ($registration->payment_status === 'completed') {
                $this->initiateRefund($registration);
            }

            $this->notificationService->sendRejectionNotification($registration, $reason);

            Log::info('Registration rejected', ['registration_id' => $registration->id, 'rejector_id' => $rejectorId]);

            return true;
        });
    }

    /**
     * Cancel a registration.
     */
    public function cancelRegistration(TrainingRegistration $registration, string $reason): bool
    {
        return DB::transaction(function () use ($registration, $reason) {
            $registration->update(['status' => 'cancelled', 'notes' => $reason]);

            if ($registration->payment_status === 'completed') {
                $this->initiateRefund($registration);
            }

            Log::info('Registration cancelled', ['registration_id' => $registration->id, 'reason' => $reason]);

            return true;
        });
    }

    /**
     * Mark registration as completed.
     */
    public function completeRegistration(
        TrainingRegistration $registration,
        float $attendancePercentage,
        ?float $score = null,
        ?bool $passed = null
    ): bool {
        return DB::transaction(function () use ($registration, $attendancePercentage, $score, $passed) {
            $registration->markCompleted($attendancePercentage, $score, $passed);

            if ($passed || $attendancePercentage >= config('training.certificate_attendance_threshold', 75)) {
                $certificationService = app(CertificationService::class);
                $certificationService->generateCertificate($registration);
            }

            Log::info('Registration completed', [
                'registration_id'       => $registration->id,
                'attendance_percentage' => $attendancePercentage,
                'score'                 => $score,
                'passed'                => $passed,
            ]);

            return true;
        });
    }

    /**
     * Get registration statistics for a package.
     */
    public function getPackageStats(TrainingPackage $package): array
    {
        $registrations = $package->registrations();

        return [
            'total_registrations' => $registrations->count(),
            'pending'             => $registrations->where('status', 'pending')->count(),
            'approved'            => $registrations->where('status', 'approved')->count(),
            'completed'           => $registrations->where('status', 'completed')->count(),
            'rejected'            => $registrations->where('status', 'rejected')->count(),
            'cancelled'           => $registrations->where('status', 'cancelled')->count(),
            'paid'                => $registrations->where('payment_status', 'completed')->count(),
            'pending_payment'     => $registrations->where('payment_status', 'pending')->count(),
            'capacity_remaining'  => $package->getRemainingCapacity(),
            'capacity_percentage' => round(($package->getEnrollmentCount() / $package->capacity) * 100, 1),
        ];
    }

    // -------------------------------------------------------------------------
    // Protected Helpers
    // -------------------------------------------------------------------------

    protected function confirmEnrollment(TrainingRegistration $registration): void
    {
        if ($registration->status !== 'approved') {
            $registration->update(['status' => 'approved', 'approved_at' => now()]);
        }

        $this->notificationService->sendEnrollmentConfirmation($registration);

        Log::info('Enrollment confirmed', [
            'registration_id' => $registration->id,
            'user_id'         => $registration->user_id,
            'package_id'      => $registration->training_package_id,
        ]);
    }

    protected function initiateRefund(TrainingRegistration $registration): bool
    {
        try {
            $user = $registration->user;

            if (!$user->phone) {
                Log::warning('Cannot refund - user has no phone number', ['registration_id' => $registration->id]);
                return false;
            }

            $refundReference = 'REFUND-' . $registration->id . '-' . time();

            $result = $this->paymentService->initiateDisbursement(
                phoneNumber: $user->phone,
                amount: $registration->amount_paid,
                reference: $refundReference,
                payerMessage: 'Refund for ' . $registration->trainingPackage->name,
                payeeNote: 'Training registration refund'
            );

            if ($result['success']) {
                $registration->update([
                    'payment_status' => 'refunded',
                    'metadata'       => array_merge($registration->metadata ?? [], [
                        'refund_reference' => $refundReference,
                        'refunded_at'      => now()->toIso8601String(),
                    ]),
                ]);

                Log::info('Refund initiated', ['registration_id' => $registration->id, 'amount' => $registration->amount_paid]);
                return true;
            }

            return false;

        } catch (\Exception $e) {
            Log::error('Refund failed', ['registration_id' => $registration->id, 'error' => $e->getMessage()]);
            return false;
        }
    }
}
