<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\ReferralEvent;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Auth\Events\Registered;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Password;
use Laravolt\Avatar\Facade as Avatar;

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;

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

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
        $this->middleware('throttle:10,1')->only('register');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param array $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        $validator = Validator::make($data, [
            // email:filter rejects CRLF/injection payloads the default rule
            // lets through on this framework version.
            'email' => ['required', 'string', 'email:filter', 'max:255', 'unique:users'],
            'password' => ['required', 'string', Password::min(10)->mixedCase()->letters()->numbers()],
            'terms' => ['accepted'],
        ]);

        $validator->after(function ($validator) use ($data) {
            $email = (string) ($data['email'] ?? '');
            $domain = strtolower(substr(strrchr($email, '@') ?: '', 1));

            if ($email !== '' && User::whereRaw('LOWER(email) = ?', [mb_strtolower($email)])->exists()) {
                $validator->errors()->add('email', 'An account already exists for this email address.');
            }

            if (site_bool('protection_block_disposable_emails', true)
                && \App\Support\DisposableEmails::isDisposable($email)) {
                $validator->errors()->add('email', 'Temporary/disposable email addresses are not allowed.');
            }

            if (!site_bool('protection_custom_email_domains')) {
                return;
            }

            $allowedDomains = site_list('protection_email_domains');

            if ($domain && $allowedDomains && !in_array($domain, $allowedDomains, true)) {
                $validator->errors()->add('email', 'Please use an allowed email provider.');
            }
        });

        return $validator;
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param array $data
     * @return \App\Models\User
     */
    protected function create(array $data)
    {
        $email = mb_strtolower(trim((string) $data['email']));
        $username = $this->generateUsername($email);
        Avatar::create($username)->save(storage_path("app/public") . "/avatars/{$username}.png", 100);
        $referrer = User::where('referral_code', request()->input('ref') ?: session('referral_code'))->first();

        $user = User::create([
            'username' => $username,
            'email' => $email,
            'password' => Hash::make($data['password']),
            'registered_ip' => ip(),
            'last_login_ip' => ip(),
            'last_seen_at' => now(),
            'user_agent' => \request()->userAgent(),
            'profile_photo_path' => "avatars/{$username}.png",
            'referred_by' => $referrer?->id,
        ]);
        $user->referralCode();

        if ($referrer && site_bool('referral_enabled', true)) {
            $signupBonus = (float) site_setting('referral_signup_bonus', config('rewards.referral_signup_bonus', 5));
            $user->addPoints((int) $signupBonus);

            ReferralEvent::create([
                'referrer_id' => $referrer->id,
                'referred_user_id' => $user->id,
                'type' => 'signup_bonus',
                'source_points' => 0,
                'reward_points' => $signupBonus,
                'meta' => ['referral_code' => $referrer->referralCode()],
            ]);

            session()->forget('referral_code');
        }

        return $user;
    }

    /**
     * Handle a registration request for the application.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
     */
    public function register(Request $request)
    {
        $request->merge(['email' => mb_strtolower(trim((string) $request->input('email')))]);
        $this->validator($request->all())->validate();

        if (site_bool('third_party_recaptcha_enabled') && !$this->verifyReCaptcha($request)) {
            return redirect()->back()
                ->withInput($request->except('password'))
                ->withErrors(['g-recaptcha-response' => 'Please complete the captcha challenge.']);
        }

        if (site_bool('protection_block_countries')) {
            $country = request_country_code();
            $blockedCountries = collect(site_list('protection_blocked_countries'))->map(fn ($country) => strtoupper($country))->all();

            if ($country && in_array($country, $blockedCountries, true)) {
                return redirect()->back()->with('error', 'Registration is not available in your country.');
            }
        }

        if (site_bool('protection_max_accounts_per_ip', true)) {
            $ip = ip();
            $maxAccounts = max(1, site_int('protection_max_accounts_count', 1));
            $accounts = User::where(function ($query) use ($ip) {
                $query->where('registered_ip', $ip)->orWhere('last_login_ip', $ip);
            })
                ->where('created_at', '>=', Carbon::now()->subWeek())
                ->count();

            if ($accounts >= $maxAccounts) {
                return redirect()->back()->with('error', 'You have reached the account limit for this IP address.');
            }
        }

        event(new Registered($user = $this->create($request->all())));

        $this->guard()->login($user);

        if ($response = $this->registered($request, $user)) {
            return $response;
        }

        return $request->wantsJson()
            ? new JsonResponse([], 201)
            : redirect($this->redirectPath());
    }

    protected function verifyReCaptcha(Request $request): bool
    {
        try {
            $response = \Illuminate\Support\Facades\Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
                'secret' => site_setting('third_party_recaptcha_secret_key', config('rewards.recaptcha_secret_key')),
                'response' => $request->input('g-recaptcha-response'),
                'remoteip' => ip(),
            ]);

            return $response->successful() && ($response->json()['success'] ?? false);
        } catch (\Throwable $exception) {
            return false;
        }
    }

    private function generateUsername(string $email): string
    {
        $localPart = Str::before($email, '@');
        $base = Str::lower((string) preg_replace('/[^a-zA-Z0-9]+/', '_', Str::ascii($localPart)));
        $base = trim($base, '_');
        $base = mb_substr($base !== '' ? $base : 'member', 0, 20);
        if (mb_strlen($base) < 3) {
            $base = 'member';
        }

        do {
            $username = $base.'_'.Str::lower(Str::random(6));
        } while (User::whereRaw('LOWER(username) = ?', [mb_strtolower($username)])->exists());

        return $username;
    }
}
