<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Http\Traits\TossTrait;
use App\Http\Traits\CryptTrait;
use App\Http\Traits\TelegramTrait;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use App\Models\DTO\ResponseData;
use App\Mail\VerifyLink;
use App\Rules\AllowNickname;
use App\Rules\NumberLength;
use App\Rules\SpecialCharLength;
use App\Rules\UppercaseLength;
use App\Rules\DeniedEmail;
use Exception;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers, TossTrait, CryptTrait, TelegramTrait;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    // 토스 인증서 본인인증 결과값
    protected array $tossData = [];

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    /**
     * Show the application registration form.
     *
     * @return \Illuminate\View\View
     */
    public function showRegistrationForm()
    {
        $policy_1 = nl2br(config('user_register_policy_1')); // 이용약관
        $policy_2 = nl2br(config('user_register_policy_1')); // 개인정보처리방침

        // 비밀번호 조건 확인
        $passwordMinLength = config('password_min_length');
        $passwordUppercaseLength = config('password_uppercase_length');
        $passwordNumbersLength = config('password_numbers_length');
        $passwordSpecialcharsLength = config('password_specialchars_length');

        $passwordGuideTip = "";
        if($passwordMinLength > 0) {
            $passwordGuideTip .= sprintf('최소 %d자 이상, ', $passwordMinLength);
        }
        if($passwordUppercaseLength > 0) {
            $passwordGuideTip .= sprintf('대문자 %d자 이상, ', $passwordUppercaseLength);
        }
        if($passwordNumbersLength > 0) {
            $passwordGuideTip .= sprintf('숫자 %d자 이상, ', $passwordNumbersLength);
        }
        if($passwordSpecialcharsLength > 0) {
            $passwordGuideTip .= sprintf('특수문자 %d자 이상, ', $passwordSpecialcharsLength);
        }
        $passwordGuideTip = rtrim($passwordGuideTip, ', ');

        return view('auth.register', [
            'policy_1' => $policy_1,
            'policy_2' => $policy_2,
            'passwordGuideTip' => $passwordGuideTip
        ]);
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email', new DeniedEmail],
            'password' => ['required', 'string', 'min:' . config('password_min_length', 4), 'confirmed', new NumberLength, new SpecialCharLength, new UppercaseLength],
            'nickname' => ['required', 'string', 'min:2', 'max:20', new AllowNickname],
            'agree_1' => 'required|numeric|in:1',
            'agree_2' => 'required|numeric|in:2'
        ], [], [
            'email' => '이메일',
            'password' => '비밀번호',
            'nickname' => '닉네임',
            'agree_1' => '이용약관 동의',
            'agree_2' => '개인정보처리방침 동의'
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Models\User
     */
    protected function create(array $data): mixed
    {
        return (new User)->register($data);
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
     */
    public function register(Request $request)
    {
        // 회원가입 차단 확인
        if (config('use_register_block')) {
            return back()->withErrors('현재 회원 신청이 차단되어 회원가입을 할 수 없습니다. 관리자에게 문의하십시오.');
        }

        $postData = $request->all();

        // 회원가입 유효성 검증
        $this->validator($postData)->validate();

        $user = $this->create($postData);

        $this->guard()->login($user); // 로그인 처리
        $user->markEmailAsVerified(); // 이메일 인증 처리

        // 회원가입 이메일 알림
        $this->registered($request, $user);

        $message = sprintf('%s 님 회원가입을 환영합니다.', $user->name ?? $user->nickname);
        return $request->wantsJson() ? new JsonResponse([], 201) : redirect($this->redirectPath())->with('message', $message);
    }

    /**
     * 이메일 검증 주소 전송
     * @method POST
     * @see /auth/register/sendVerifyLink
     */
    public function sendVerifyLink(Request $request, ResponseData $response): ResponseData
    {
        try {

            $email = $request->post('email');

            if (!$email) {
                throw new Exception('이메일을 입력해주세요.');
            }

            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                throw new Exception('이메일 형식이 아닙니다.');
            }

            // 중복 여부
            if ((new User)->where('email', $email)->exists()) {
                throw new Exception('이미 사용 중인 이메일입니다.');
            }

            // 유효성 확인
            if (!(new DeniedEmail)->passes(null, $email)) {
                throw new Exception('입력하신 이메일은 사용하실 수 없습니다.');
            }

            $token = sha1($email);
            $verifyExpireTime = Carbon::now()->addMinutes(VERIFY_EXPIRES_AT);
            $verifyLink = URL::temporarySignedRoute(
                'auth.register.verifyEmail', $verifyExpireTime, [
                    'token' => $token
                ]
            );

            // 인증 메일 전송
            Mail::to($email)->send(new VerifyLink($verifyLink, VERIFY_EXPIRES_AT, $email));

            // 인증 메일 캐시 저장
            Cache::put('verifyEmailToken_' . $token, $token, $verifyExpireTime);
            Cache::put('verifyEmailStatus_' . $token, 0);

            return $response;
        } catch (Exception $e) {
            return $response::fromException($e);
        }
    }

    /**
     * 이메일 검증 확인
     * @method GET
     * @see /auth/register/verifyEmail/{token}
     */
    public function verifyEmail(Request $request)
    {
        if (!$request->hasValidSignature()) {
            abort(404);
        }

        $token = (string)$request->route('token');

        if (!hash_equals((string)Cache::get('verifyEmailToken_' . $token), $token)) {
            abort(403);
        }

        Cache::put('verifyEmailStatus_' . $token, 1);

        return alertClose('이메일 인증이 완료되었습니다.');
    }

    /**
     * 이메일 인증 여부 확인
     * @method GET
     * @see /auth/register/checkVerifiedEmail
     */
    public function checkVerifiedEmail(Request $request): string
    {
        return json_encode([
            'success' => intval(Cache::get('verifyEmailStatus_' . sha1($request->post('email'))))
        ]);
    }

    /**
     * 회원가입 후 처리
     */
    public function registered(Request $request, $user)
    {
        $this->sendMessageToRegister($user);
    }
}
