<?php

namespace Modules\SmartStorage\Entities\Services;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\SmartStorage\Entities\SsOtpSession;
use Modules\SmartStorage\Entities\SsSetting;

/**
 * One-time-password gate for opening a storage-access session.
 *
 * A user verifies a short code once; this opens a time-boxed session (bound to
 * user + device) during which guarded files are served without re-prompting.
 * Codes are delivered over the best available channel (WhatsApp module → mail →
 * log) and only their hash is stored.
 */
class OtpService
{
    public function __construct(protected DeviceManager $devices)
    {
    }

    /** Is there a currently-valid (verified, unexpired) session for this user+device? */
    public function hasLiveSession(Request $request): bool
    {
        $userId = auth()->id();
        if (!$userId) {
            return false;
        }

        return SsOtpSession::where('user_id', $userId)
            ->where('device_fingerprint', $this->devices->fingerprintHash())
            ->whereNotNull('verified_at')
            ->where('expires_at', '>', now())
            ->exists();
    }

    /**
     * Generate, store and deliver a fresh code. Returns the plaintext code only
     * in non-production (so it can be surfaced for testing); otherwise null.
     */
    public function start(Request $request): ?string
    {
        $settings = SsSetting::current();
        $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);

        // Verification window: 10 minutes to enter the code.
        SsOtpSession::create([
            'business_id'        => $settings->business_id,
            'user_id'            => auth()->id(),
            'device_fingerprint' => $this->devices->fingerprintHash(),
            'code_hash'          => Hash::make($code),
            'ip'                 => $request->ip(),
            'expires_at'         => now()->addMinutes(10),
        ]);

        $this->deliver($settings, $code);

        return app()->environment('production') ? null : $code;
    }

    /**
     * Verify a submitted code. On success the session becomes live for the
     * configured TTL and true is returned.
     */
    public function verify(Request $request, string $code): bool
    {
        $session = SsOtpSession::where('user_id', auth()->id())
            ->where('device_fingerprint', $this->devices->fingerprintHash())
            ->whereNull('verified_at')
            ->where('expires_at', '>', now())
            ->latest('id')
            ->first();

        if (!$session) {
            return false;
        }

        $session->increment('attempts');
        if ($session->attempts > 6) {
            return false;
        }

        if (!Hash::check($code, $session->code_hash)) {
            return false;
        }

        $ttl = (int) (SsSetting::current()->otp_ttl_minutes ?: 15);
        $session->verified_at = now();
        $session->expires_at = now()->addMinutes($ttl);
        $session->save();

        return true;
    }

    protected function deliver(SsSetting $settings, string $code): void
    {
        $user = auth()->user();
        $channel = $settings->otp_channel ?: 'auto';
        $message = "Your storage access code is: {$code}";

        try {
            if (($channel === 'auto' || $channel === 'whatsapp') && $this->tryWhatsApp($user, $message)) {
                return;
            }

            if (($channel === 'auto' || $channel === 'mail') && $user && !empty($user->email)) {
                Mail::raw($message, function ($m) use ($user) {
                    $m->to($user->email)->subject('Storage access code');
                });
                return;
            }
        } catch (\Throwable $e) {
            Log::warning('SmartStorage OTP delivery failed: ' . $e->getMessage());
        }

        // Last resort (and the path in dev): write to the log.
        Log::info("SmartStorage OTP for user " . (auth()->id() ?? '?') . ": {$code}");
    }

    /**
     * Best-effort WhatsApp delivery via the WhatsApp module if it is installed.
     * Returns false (so the caller falls back) if the module/contact is absent.
     */
    protected function tryWhatsApp($user, string $message): bool
    {
        if (!class_exists(\Nwidart\Modules\Facades\Module::class)) {
            return false;
        }
        if (!\Module::has('WhatsApp') || !optional(\Module::find('WhatsApp'))->isEnabled()) {
            return false;
        }
        $phone = $user->contact_number ?? null;
        if (empty($phone)) {
            return false;
        }

        // The WhatsApp module exposes a queued send service; resolve defensively.
        $serviceClass = '\\Modules\\WhatsApp\\Entities\\Services\\WhatsAppService';
        if (!class_exists($serviceClass)) {
            return false;
        }
        try {
            app($serviceClass)->sendText($phone, $message);
            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }
}
