<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\WorkspaceInvitation;
use App\Services\AuditLogger;
use App\Services\NotificationDispatcher;
use App\Support\AppMode;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Invite acceptance — the set-password landing for an admin-invited (Δ20)
 * or owner-invited (future SaaS) member. The accept link carries the raw
 * token; we hash + match it against `workspace_invitations.token`.
 *
 * Δ16: accepting the invite IS email verification — the invitee proved
 * ownership of the address by receiving the link at it, so we stamp
 * `email_verified_at = now()` and skip the separate verify-notice gate.
 */
class AcceptInviteController extends Controller
{
    public function show(Request $request, string $token): Response|RedirectResponse
    {
        $invite = $this->resolveValidInvite($token);

        if ($invite === null) {
            return redirect()->route('login')
                ->with('status', 'That invitation link is invalid or has expired.');
        }

        return Inertia::render('Auth/AcceptInvite', [
            'token' => $token,
            'email' => $invite->email,
            'role' => $invite->role,
            // Distinguish an operator-CREATED SaaS account (registration
            // framing) from a team invite. SaaS create stamps role 'owner';
            // pool invites are editor/viewer.
            'purpose' => (AppMode::isSaas() && $invite->role === 'owner') ? 'account' : 'invite',
        ]);
    }

    public function accept(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'token' => ['required', 'string'],
            'password' => ['required', 'string', 'min:8', 'max:191', 'confirmed'],
        ]);

        $invite = $this->resolveValidInvite($data['token']);

        if ($invite === null) {
            throw ValidationException::withMessages([
                'token' => ['That invitation link is invalid or has expired.'],
            ]);
        }

        $user = User::query()->where('email', $invite->email)->first();

        if ($user === null) {
            // The pending user row was deleted between invite + accept — the
            // invite is stale. Bounce rather than silently creating a user
            // (admin-invite is the creation path; accept only finalizes it).
            throw ValidationException::withMessages([
                'token' => ['That invitation is no longer valid. Ask an admin to re-invite you.'],
            ]);
        }

        if ($user->disabled_at !== null) {
            throw ValidationException::withMessages([
                'token' => ['This account is disabled. Contact an admin.'],
            ]);
        }

        $alreadyVerified = $user->email_verified_at !== null;

        DB::transaction(function () use ($user, $data, $invite): void {
            $user->forceFill([
                'password' => Hash::make($data['password']),
                // Δ16 — the accept link is the email verification.
                'email_verified_at' => $user->email_verified_at ?? now(),
                'remember_token' => Str::random(60),
            ])->save();

            $invite->forceFill(['accepted_at' => now()])->save();
        });

        Auth::login($user);
        $request->session()->regenerate();

        AuditLogger::record(
            'user.invite.accepted',
            $user,
            "{$user->email} accepted their invitation",
            ['email' => $user->email, 'role' => $user->role],
        );

        // First-time welcome — only when this accept also performed the
        // verification (avoids re-welcoming a re-accept of an already-active
        // address).
        if (! $alreadyVerified) {
            NotificationDispatcher::userWelcome($user);
        }

        return redirect('/projects')->with('success', 'Welcome aboard — your account is ready.');
    }

    /**
     * Hash the raw token, look up a matching invitation that's un-accepted
     * and un-expired. Returns null on any miss (don't distinguish invalid vs
     * expired to the caller — both are dead links).
     */
    private function resolveValidInvite(string $rawToken): ?WorkspaceInvitation
    {
        if ($rawToken === '') {
            return null;
        }

        return WorkspaceInvitation::query()
            ->where('token', hash('sha256', $rawToken))
            ->whereNull('accepted_at')
            ->where(function ($w) {
                $w->whereNull('expires_at')->orWhere('expires_at', '>', now());
            })
            ->first();
    }
}
