<?php

namespace App\Http\Controllers;

use App\Gates\UserGate;
use App\Mail\Verify;
use App\Repos\CreditRepo;
use App\Repos\PlanRepo;
use App\Repos\UserRepo;
use App\Services\ResponseService;
use App\Services\ValidationService;
use App\Utils\OtpUtil;
use App\Utils\UuidUtil;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Throwable;

class UserController extends Controller
{
    public function signUp(Request $request)
    {
        try {
            $rules = [
                'name' => ['required', 'string'],
                'email' => ['required', 'email', 'unique:users,email'],
            ];
            ValidationService::validate($request->all(), $rules);
            $plan = PlanRepo::firstByNameOrFail('free');
            $userData = [
                'plan_id' => $plan->id,
                'name' => $request->name,
                'email' => $request->email,
                'auth_token' => UuidUtil::create(),
                'verify_token' => UuidUtil::create(),
                'otp' => OtpUtil::create(),
            ];
            $user = UserRepo::create($userData);
            $plan = PlanRepo::firstByNameOrFail('free');
            $creditData = [
                'user_id' => $user->id,
                'public_page' => $plan->public_page_limit,
                'page_creation' => $plan->page_creation_limit,
            ];
            CreditRepo::create($creditData);
            Mail::to($user)->send(new Verify($user));
            $result = ['token' => $user->verify_token];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function signIn(Request $request)
    {
        try {
            $rules = [
                'email' => ['required', 'email', 'exists:users,email'],
            ];
            ValidationService::validate($request->all(), $rules);
            $user = UserRepo::firstByEmailOrFail($request->email);
            UserGate::canAuth($user);
            $otp = OtpUtil::create();
            $user = UserRepo::updateOtpOrFail($user->id, $otp);
            Mail::to($user)->send(new Verify($user));
            $result = ['token' => $user->verify_token];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function verify(Request $request)
    {
        try {
            $rules = [
                'token' => ['required', 'exists:users,verify_token'],
                'otp' => ['required', 'integer'],
            ];
            ValidationService::validate($request->all(), $rules);
            try {
                $user = UserRepo::firstByVerifyTokenAndOtpOrFail($request->token, $request->otp);
            } catch (Throwable $error) {
                $message = 'Invalid or expired verification request';
                return ResponseService::unprocessable($message);
            }
            UserRepo::clearOtpOrFail($user->id);
            $result = [
                'authToken' => $user->auth_token,
                'admin' => $user->admin
            ];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function read(Request $request)
    {
        try {
            $user = $request->user();
            $result = UserRepo::read($user->id);
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function update(Request $request)
    {
        try {
            $user = $request->user();
            $rules = [
                'name' => ['required', 'string'],
                'photo' => ['required', 'string'],
                'email' => ['required', 'email', "unique:users,email,$user->id"],
            ];
            ValidationService::validate($request->all(), $rules);
            $userData = [
                'name' => $request->name,
                'photo' => $request->photo,
                'email' => $request->email,
            ];
            UserRepo::updateByIdOrFail($user->id, $userData);
            $result = ['updated' => true];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function paginate(Request $request)
    {
        try {
            $rules = [
                'page' => ['required', 'integer', 'min:1'],
                'search' => ['nullable', 'string'],
            ];
            ValidationService::validate($request->query(), $rules);
            $result = UserRepo::paginate(
                $request->query('page'),
                $request->query('search')
            );
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function toggleBlocked(int $id)
    {
        try {
            $rules = [
                'id' => ['required', 'exists:users,id'],
            ];
            ValidationService::validate(compact('id'), $rules);
            UserRepo::toggleBlockedOrFail($id);
            $result = ['toggled' => true];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function createAdmin(Request $request)
    {
        try {
            $rules = [
                'name' => ['required', 'string'],
                'email' => ['required', 'email', 'unique:users,email'],
            ];
            ValidationService::validate($request->all(), $rules);
            if (UserRepo::existAdmin()) {
                return ResponseService::bad('Admin already exists');
            };
            $userData = [
                'name' => $request->name,
                'email' => $request->email,
                'auth_token' => UuidUtil::create(),
                'verify_token' => UuidUtil::create(),
                'otp' => OtpUtil::create(),
                'admin' => true,
            ];
            $user = UserRepo::create($userData);
            Mail::to($user)->send(new Verify($user));
            $result = ['token' => $user->verify_token];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }
}
