<?php

namespace App\Actions\Fortify;

use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
use Laravel\Fortify\Contracts\CreatesNewUsers;

class CreateNewUser implements CreatesNewUsers
{
    /**
     * Per-request correlation ID + start time so every log line from a
     * single registration ties together. Grep `[RegFlow]` in laravel.log
     * to follow the trace; grep `req=<id>` to isolate one specific
     * registration when multiple users sign up at once.
     */
    protected string $reqId;
    protected float $start;

    public function create(array $input): User
    {
        $this->reqId = substr(bin2hex(random_bytes(4)), 0, 8);
        $this->start = microtime(true);

        // Version marker — bump this string whenever you redeploy
        // CreateNewUser so you can confirm the running PHP process is
        // actually executing the new code (opcache + PHP-FPM happily
        // serve stale bytecode for hours after a file change without
        // a graceful reload). Grep `[RegFlow] BUILD=` to verify.
        $this->stamp('ENTER  BUILD=2026-05-27-A  email=' . ($input['email'] ?? 'none'));

        // Honor Admin → Settings → Auth → "Allow user registration" toggle.
        if (\App\Models\SystemSetting::get('registration_enabled', 'true') !== 'true') {
            $this->stamp('ABORT  registration_disabled');
            abort(403, __('Registration is currently disabled.'));
        }
        $this->stamp('OK     registration_enabled check passed');

        try {
            Validator::make($input, [
                'name' => ['required', 'string', 'min:2', 'max:100'],
                'email' => ['required', 'string', 'email', 'max:255', Rule::unique(User::class)],
                'password' => ['required', 'string', 'confirmed', Password::min(10)->mixedCase()->numbers()->symbols()],
                'terms' => ['accepted'],
            ])->validate();
            $this->stamp('OK     validation passed');
        } catch (\Throwable $e) {
            $this->stamp('FAIL   validation: ' . substr($e->getMessage(), 0, 200));
            throw $e;
        }

        // FIX-120: Validate referral code exists if provided
        $referredBy = $input['referral_code'] ?? null;
        if ($referredBy) {
            $referrerExists = User::where('referral_code', strtoupper(trim($referredBy)))
                ->where('status', 'active')
                ->exists();
            if (!$referrerExists) {
                $referredBy = null;
            }
            $this->stamp('OK     referral check ref=' . ($referredBy ?: 'invalid'));
        }

        $this->stamp('STEP   User::create() about to run');
        $user = User::create([
            'name' => $input['name'],
            'email' => strtolower($input['email']),
            'password' => $input['password'],
            'referral_code' => strtoupper(\Illuminate\Support\Str::random(16)),
            'referred_by' => $referredBy,
        ]);
        $this->stamp('OK     User::create() done user_id=' . $user->id);

        // Welcome email is deferred to a terminating callback that fires
        // AFTER the HTTP response has been flushed to the browser.
        //
        // Why not ShouldQueue? Because the queue dispatch itself writes
        // to the queue connection (Redis on most installs). If Redis is
        // dead, the dispatch hangs for 60+ seconds before the email even
        // reaches SMTP — that's the same 504 problem from a different
        // angle.
        //
        // app()->terminating() lets PHP-FPM finish the response, then
        // runs the callback in the SAME worker after the user has
        // already been redirected. If the SMTP send hangs, only one
        // FPM worker is briefly stuck — the customer sees an instant
        // /onboarding/step-1 landing.
        //
        // default_socket_timeout is shortened to 10s for the duration
        // of this call so even a totally-dead SMTP host frees the
        // worker within 10s instead of PHP's default 60s.
        if ($this->canSendMail()) {
            $this->stamp('STEP   scheduling welcome email via terminating() callback');
            app()->terminating(function () use ($user) {
                $previousTimeout = ini_get('default_socket_timeout');
                @ini_set('default_socket_timeout', '10');
                try {
                    $user->notify(new \App\Notifications\WelcomeNotification());
                    Log::info('[RegFlow] welcome email sent post-response user_id=' . $user->id);
                } catch (\Throwable $e) {
                    Log::warning('[RegFlow] welcome email failed post-response user_id=' . $user->id . ' err=' . substr($e->getMessage(), 0, 200));
                } finally {
                    @ini_set('default_socket_timeout', (string) $previousTimeout);
                }
            });
            $this->stamp('OK     terminating() callback scheduled');
        } else {
            $this->stamp('SKIP   welcome email (mail not configured)');
        }

        $this->stamp('EXIT   returning user');
        return $user;
    }

    /**
     * Single-line log helper. Every entry carries the per-request id +
     * elapsed milliseconds since ENTER, so you can spot the gap that
     * caused the 504 just by reading the log top-to-bottom.
     */
    protected function stamp(string $msg): void
    {
        $elapsedMs = (int) ((microtime(true) - $this->start) * 1000);
        Log::info("[RegFlow] req={$this->reqId} +{$elapsedMs}ms {$msg}");
    }

    /**
     * Returns true only when the install has enough mail config to plausibly
     * deliver. Anything else (log driver on a fresh install, missing host,
     * missing from-address) → bail out so registration is never blocked by
     * mail-related issues.
     */
    protected function canSendMail(): bool
    {
        $driver = strtolower((string) config('mail.default'));

        // log/array drivers don't send real mail — no point queueing.
        if (in_array($driver, ['log', 'array', 'null', ''], true)) {
            return false;
        }

        // SMTP requires both a host AND a from-address. Without either,
        // Symfony Mailer would either fail validation or attempt a connect
        // to an empty host and stall.
        if ($driver === 'smtp') {
            $host = trim((string) config('mail.mailers.smtp.host'));
            $from = trim((string) config('mail.from.address'));
            if ($host === '' || $from === '') {
                return false;
            }
        }

        return true;
    }
}
