<?php

namespace App\Http\Controllers;

use App\Enums\OtpVerificationResult;
use App\Http\Requests\SendOtpRequest;
use App\Http\Requests\VerifyOtpRequest;
use App\Models\Coupon;
use App\Services\CouponService;
use App\Services\OtpService;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Cookie;

class VerificationController extends Controller
{
    private const DEVICE_COOKIE_NAME = 'device_registered';

    public function __construct(
        private OtpService $otpService,
        private CouponService $couponService,
    ) {}

    public function index(): View|RedirectResponse
    {
        if ($redirect = $this->redirectIfAlreadyRegistered()) {
            return $redirect;
        }

        return view('verification.index');
    }

    public function sendOtp(SendOtpRequest $request): RedirectResponse
    {
        if ($request->cookie(self::DEVICE_COOKIE_NAME)) {
            return back()->withErrors(['phone' => 'Siz eýýäm agza bolduňyz.']);
        }

        $phone = $request->unmaskedPhone();

        $existingCoupon = Coupon::query()->where('phone', $phone)->first();
        if ($existingCoupon) {
            return back()->withErrors(['phone' => 'Bu telefon belgisi eýýäm ulanyldy.'])->withInput();
        }

        if (! $this->otpService->issue($phone)) {
            return back()->withErrors(['phone' => 'SMS iberilmedi. Soňrak synanyşyň.'])->withInput();
        }

        session()->put('verify_phone', $phone);

        return redirect()->route('verification.verify.form');
    }

    public function verifyForm(): View|RedirectResponse
    {
        if ($redirect = $this->redirectIfAlreadyRegistered()) {
            return $redirect;
        }

        if (! session()->has('verify_phone')) {
            return redirect()->route('verification.index');
        }

        return view('verification.verify', [
            'phone' => session()->get('verify_phone'),
        ]);
    }

    public function resendOtp(Request $request): RedirectResponse
    {
        if ($request->cookie(self::DEVICE_COOKIE_NAME)) {
            return redirect()->route('verification.congratulations');
        }

        $phone = session()->get('verify_phone');

        if (! $phone) {
            return redirect()->route('verification.index');
        }

        if (Coupon::query()->where('phone', $phone)->exists()) {
            return redirect()->route('verification.congratulations');
        }

        if (! $this->otpService->issue($phone)) {
            return back()->withErrors(['otp' => 'SMS iberilmedi. Soňrak synanyşyň.']);
        }

        return back()->with('status', 'Täze kod iberildi!');
    }

    public function verifyOtp(VerifyOtpRequest $request): RedirectResponse
    {
        $phone = session()->get('verify_phone');

        if (! $phone) {
            return redirect()->route('verification.index');
        }

        $result = $this->otpService->verify($phone, $request->string('otp')->toString());

        if ($result === OtpVerificationResult::Expired) {
            return back()->withErrors(['otp' => 'Kodyň möhleti gutardy.']);
        }

        if ($result === OtpVerificationResult::Invalid) {
            return back()->withErrors(['otp' => 'Nädogry kod.']);
        }

        $coupon = $this->couponService->findOrCreateForPhone($phone);

        session()->put('coupon_code', $coupon->code);

        return redirect()
            ->route('verification.congratulations')
            ->withCookie($this->makeDeviceCookie($phone));
    }

    public function congratulations(): View|RedirectResponse
    {
        if (! session()->has('coupon_code') && ! request()->cookie(self::DEVICE_COOKIE_NAME)) {
            return redirect()->route('verification.index');
        }

        $sessionCode = session()->get('coupon_code');
        $code = null;

        if ($sessionCode && Coupon::query()->where('code', $sessionCode)->exists()) {
            $code = $sessionCode;
        } elseif ($sessionCode) {
            session()->forget('coupon_code');
        }

        if (! $code) {
            $phone = $this->phoneFromDeviceCookie();

            if ($phone) {
                $coupon = Coupon::query()->where('phone', $phone)->first();

                if ($coupon) {
                    $code = $coupon->code;
                    session()->put('coupon_code', $code);
                }
            }
        }

        if (! $code && session()->has('verify_phone')) {
            $coupon = Coupon::query()->where('phone', session()->get('verify_phone'))->first();

            if ($coupon) {
                $code = $coupon->code;
                session()->put('coupon_code', $code);
            }
        }

        if (! $code) {
            return redirect()
                ->route('verification.index')
                ->withoutCookie(self::DEVICE_COOKIE_NAME);
        }

        return view('verification.congratulations', [
            'code' => $code,
        ]);
    }

    private function redirectIfAlreadyRegistered(): ?RedirectResponse
    {
        if (request()->cookie(self::DEVICE_COOKIE_NAME) || session()->has('coupon_code')) {
            return redirect()->route('verification.congratulations');
        }

        return null;
    }

    private function makeDeviceCookie(string $phone): Cookie
    {
        return cookie(
            self::DEVICE_COOKIE_NAME,
            encrypt($phone),
            60 * 24 * 365 * 5,
            '/',
            null,
            null,
            true,
            false,
            'lax',
        );
    }

    private function phoneFromDeviceCookie(): ?string
    {
        $value = request()->cookie(self::DEVICE_COOKIE_NAME);

        if (! $value) {
            return null;
        }

        try {
            $phone = decrypt($value);

            return is_string($phone) && preg_match('/^6\d{7}$/', $phone) ? $phone : null;
        } catch (\Throwable) {
            return null;
        }
    }
}
