<?php

namespace App\Service\User;

use App\Enum\UserType;
use App\Helpers\CodeGenerator;
use App\Helpers\MessageContent;
use App\Models\ReferralHistory;
use App\Models\SystemSetting;
use App\Models\User;
use App\Models\Vendor;
use App\Models\Newsletter;
use App\Service\MailService;
use App\Traits\JsonResponseTrait;
use Carbon\Carbon;
use Date;
use DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use Log;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Str;

class AuthService
{
    use JsonResponseTrait;
    protected $mailService;

    public function __construct(MailService $mailService)
    {
        $this->mailService = $mailService;
    }

    public function signup(array $data)
    {
        $email = trim(strtolower($data['email']));
        $isEmailExist = User::where('email', $email)->first();

        if ($isEmailExist) {
            return $this->errorResponse(statusCode: 400, message: 'Account with this email already exists.');
        }

        if (!empty($data['phone'])) {
            $phone = trim($data['phone']);
            $isPhoneExist = User::where('phone', $phone)->first();

            if ($isPhoneExist) {
                return $this->errorResponse(statusCode: 400, message: 'Account with this phone number already exists.');
            }
        }

        DB::beginTransaction();

        try {
            $phone = isset($data['phone']) ? trim($data['phone']) : null;

            $user = User::create([
                'firstname' => $data['firstname'],
                'lastname' => $data['lastname'],
                'email' => $email,
                'phone' => $phone,
                'password' => Hash::make($data['password']),
                'state' => $data['state'] ?? null,
                'status' => 0,
                'user_type' => UserType::CUSTOMER->value, // Default to customer if not specified

                'otp' => random_int(100000, 999999),
                'otp_reason' => 'registration',
                'otp_expiry_at' => now()->addMinutes(10),
                'otp_ver_at' => null,

                'email_ver' => false,
                'email_ver_at' => null,

                'phone_ver' => false,
                'phone_ver_at' => null,

                'invalid_pin_tries' => 0,
                'invalid_pin_tries_at' => null,
            ]);

            // Create profile based on user_type
            if ($user->user_type === UserType::ADMIN->value) {
                $this->createAdminProfile($user, $data);
            } elseif ($user->user_type === UserType::VENDOR->value) {
                $this->createVendorProfile($user, $data);
            }

            DB::commit();

            // Send welcome email
            // $this->mailService->sendEmail($user->email, 'Welcome!', MessageContent::welcomeEmail($user->name));

            // Send OTP email
            // $this->mailService->sendEmail($user->email, 'Verify Your Account', MessageContent::verificationOtpEmail($user->otp));

            return $this->successDataResponse(data: $user, message: "User registered successfully");
        } catch (\Exception $e) {
            DB::rollBack();

            Log::error('User signup failed: ' . $e->getMessage(), [
                'email' => $email,
                'exception' => $e,
            ]);

            return $this->errorResponse(statusCode: 500, message: "An error occurred during registration.");
        }
    }

    /**
     * Create admin profile for user
     */
    private function createAdminProfile(User $user, array $data)
    {
        $admin = \App\Models\Admin::create([
            'user_id' => $user->id,
            'username' => $data['username'] ?? explode('@', $user->email)[0],
            'status' => 'active',
            'admin_level' => $data['admin_level'] ?? 1,
            'role' => $data['role'] ?? 'admin',
        ]);

        return $admin;
    }

    /**
     * Create vendor profile for user
     */
    private function createVendorProfile(User $user, array $data)
    {
        $vendor = \App\Models\Vendor::create([
            'user_id' => $user->id,
            'business_name' => $data['business_name'] ?? $user->name,
            'email' => $data['business_email'] ?? $user->email,
            'phone' => $data['business_phone'] ?? $user->phone,
            'slug' => $data['slug'] ?? Str::slug($data['business_name'] ?? $user->name),
            'status' => 'pending',
        ]);

        return $vendor;
    }


    public function login(array $input)
    {
        // Check for email or phone
        if (!isset($input['email']) && !isset($input['phone'])) {
            return $this->errorResponse(statusCode: 400, message: 'Either email or phone is required.');
        }

        if (!isset($input['password']) || empty($input['password'])) {
            return $this->errorResponse(statusCode: 400, message: 'Password is required.');
        }

        $credentialKey = isset($input['email']) ? 'email' : 'phone';
        $credentialValue = trim(strtolower($input[$credentialKey]));
        $password = $input['password'];

        // Find User by email or phone, optionally filter by user_type
        $query = User::where($credentialKey, $credentialValue);

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

        $user = $query->first();

        if (!$user || !Hash::check($password, $user->password)) {
            return $this->errorResponse(statusCode: 401, message: 'Invalid credentials.');
        }

        // Generate JWT token
        $token = JWTAuth::fromUser($user);

        return $this->successDataResponse(
            data: [
                'user' => $user,
                'accessToken' => $token,
            ],
            message: "Login successful"
        );
    }


    /**
     * Account verification (e.g., email verification)
     */
    public function verifyAccount(array $input)
    {
        $token = trim($input['otp']);

        // Determine if verification is via email or phone
        if (isset($input['email'])) {
            $user = User::where('email', trim(strtolower($input['email'])))->first();
            $verificationField = 'email';
            $verifiedFlag = 'email_ver';
            $verifiedAtField = 'email_ver_at';
        } elseif (isset($input['phone'])) {
            $user = User::where('phone', trim($input['phone']))->first();
            $verificationField = 'phone';
            $verifiedFlag = 'phone_ver';
            $verifiedAtField = 'phone_ver_at';
        } else {
            return $this->errorResponse(statusCode: 400, message: 'Email or phone is required for verification');
        }

        if (!$user) {
            return $this->errorResponse(statusCode: 404, message: "User with provided $verificationField not found");
        }

        if ($user->$verifiedFlag) {
            return $this->errorResponse(statusCode: 400, message: "Account already verified via $verificationField");
        }

        if ($user->otp !== $token) {
            return $this->errorResponse(statusCode: 400, message: 'Invalid otp code');
        }

        if (Carbon::parse($user->otp_expiry_at)->isPast()) {
            return $this->errorResponse(statusCode: 410, message: 'OTP code has expired, kindly generate another OTP');
        }

        $user->update([
            $verifiedFlag => true,
            $verifiedAtField => now(),
            'otp' => null,
            'otp_expiry_at' => null,
            'otp_reason' => null
        ]);

        return $this->successResponse(200, "Account verified successfully via $verificationField");
    }


    /**
     * Resend OTP for verificationn (e.g., emailverification, phoneverificaion)
     */
    public function resendOtp(array $input)
    {
        $reason = $input['reason'] ?? null;

        // Determine if input is email or phone
        if (isset($input['email'])) {
            $identifier = trim(strtolower($input['email']));
            $user = User::where('email', $identifier)->first();
            $fieldType = 'email';
        } elseif (isset($input['phone'])) {
            $identifier = trim($input['phone']);
            $user = User::where('phone', $identifier)->first();
            $fieldType = 'phone';
        } else {
            return $this->errorResponse(statusCode: 400, message: 'Email or phone is required to resend OTP');
        }

        if (!$user) {
            return $this->errorResponse(statusCode: 404, message: "User with provided $fieldType not found");
        }

        // Validate the reason
        $validReasons = ['registration', 'password_reset', 'two_factor', 'other'];
        if (!in_array($reason, $validReasons)) {
            return $this->errorResponse(statusCode: 422, message: 'Invalid OTP reason');
        }

        // Generate OTP
        $otp = random_int(100000, 999999);

        // Update OTP fields in DB
        $user->otp = $otp;
        $user->otp_reason = $reason;
        $user->otp_expiry_at = now()->addMinutes(10); // Expiry time
        $user->save();

        return $this->successResponse(
            200,
            "OTP sent successfully to your $fieldType"
        );
    }


    public function forgotPassword(string $identifier)
    {
        // Determine whether the identifier is an email or a phone number
        if (filter_var($identifier, FILTER_VALIDATE_EMAIL)) {
            $user = User::where('email', strtolower(trim($identifier)))->first();
            $fieldType = 'email';
        } else {
            $user = User::where('phone', trim($identifier))->first();
            $fieldType = 'phone';
        }

        if (!$user) {
            return $this->errorResponse(statusCode: 404, message: "User with provided $fieldType not found");
        }

        // Generate a 6-digit OTP
        $otp = random_int(100000, 999999);

        // Set OTP expiration time (10 minutes from now)
        $otpExpireAt = now()->addMinutes(10);

        // Save OTP and related information to the user
        $user->update([
            'otp' => $otp,
            'otp_expiry_at' => $otpExpireAt,
            'otp_reason' => 'password_reset',
        ]);

        // TODO: Send OTP via email or SMS
        // Example:
        // if ($fieldType === 'email') {
        //     Mail::to($user->email)->send(new PasswordResetOtpMail($otp));
        // } else {
        //     SmsService::send($user->phone, "Your OTP is $otp");
        // }

        return $this->successDataResponse(
            data: ['otp_expiration_time' => $otpExpireAt],
            message: "OTP has been sent to your $fieldType for password reset"
        );
    }


    public function resetPassword(array $input)
    {
        $newPassword = $input['password'];
        $token = $input['otp'];

        // Determine if it's email or phone
        if (isset($input['email'])) {
            $identifier = strtolower(trim($input['email']));
            $user = User::where('email', $identifier)->first();
            $fieldType = 'email';
        } elseif (isset($input['phone'])) {
            $identifier = trim($input['phone']);
            $user = User::where('phone', $identifier)->first();
            $fieldType = 'phone';
        } else {
            return $this->errorResponse(statusCode: 400, message: 'Email or phone is required for password reset');
        }

        if (!$user) {
            return $this->errorResponse(statusCode: 404, message: "User with provided $fieldType not found");
        }

        if ($user->otp !== $token) {
            return $this->errorResponse(statusCode: 400, message: 'Invalid otp code');
        }

        if (Carbon::parse($user->otp_expiry_at)->isPast()) {
            return $this->errorResponse(410, 'OTP code has expired, kindly generate another OTP Code');
        }

        $password = Hash::make($newPassword);

        $user->update([
            'password' => $password,
            'otp' => null,
            'otp_expires_at' => null,
            'otp_reason' => null,
            'otp_request_at' => null,
        ]);

        return $this->successResponse(200, "Password reset successful");
    }


    public function addUserToNewsletter(string $email)
    {
        try {;

            // Check if already subscribed
            $existing = Newsletter::where('email', $email)->first();
            if ($existing) {
                return $this->errorResponse(statusCode: 400, message: 'Email is already subscribed.');
            }

            // Save to newsletter table
            Newsletter::create([
                'email' => $email,
            ]);

            // Send confirmation mail
            $welcomeSubject = 'Thank you for subscribing!';
            $welcomeMessageContent = MessageContent::newsletterEmail();
            $this->mailService->sendEmail($email, $welcomeSubject, $welcomeMessageContent);

            return $this->successResponse(message: 'Thanks for subscribing!');
        } catch (\Exception $e) {
            Log::error('Newsletter subscription failed: ' . $e->getMessage());
            return $this->errorResponse(statusCode: 500, message: 'Something went wrong. Try again.');
        }
    }

    public function vendorApplication(array $data)
    {
        $email = $data['email'] ?? null;

        try {
            // Check if the email already exists
            $isEmailExist = Vendor::where('email', $email)->first();
            if ($isEmailExist) {
                return $this->errorResponse(statusCode: 400, message: 'Email has already been used to apply');
            }

            // Generate a unique track_id
            $data['track_id'] = strtoupper((string) Str::uuid());

            // Handle file uploads if present
            if (isset($data['pricing_file'])) {
                // Store the pricing file and get the path
                $data['pricing_file'] = $data['pricing_file']->store('uploads/pricing', 'public');
            }

            if (isset($data['cac_document'])) {
                // Store the CAC document and get the path
                $data['cac_document'] = $data['cac_document']->store('uploads/cac', 'public');
            }

            // Start a database transaction
            DB::beginTransaction();

            // Create the new vendor record
            $application = Vendor::create($data);

            // Commit the transaction
            DB::commit();

            // Return a success response
            return $this->successDataResponse(data: $application, message: 'Application submitted successfully');
        } catch (\Exception $e) {
            // Rollback in case of an error
            DB::rollBack();

            // Log the error
            Log::error('Vendor application failed: ' . $e->getMessage(), [
                'email' => $email,
                'exception' => $e
            ]);

            // Return an error response
            return $this->errorResponse(statusCode: 500, message: 'Something went wrong during application. Please try again.');
        }
    }
}
