import type { FormComponentRef } from '@inertiajs/core';
import { Form, Head, router, usePage } from '@inertiajs/react';
import {
    Eye,
    EyeOff,
    Info,
    KeyRound,
    LogOut,
    Mail,
    PanelLeftClose,
    PanelLeftOpen,
    Pencil,
    Shield,
    UserRound,
} from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { toast, Toaster } from 'sonner';
import { AppSidebar } from '@/components/AppSidebar';
import { Button } from '@/components/ui/button';
import { useAppSidebar } from '@/hooks/useAppSidebar';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
} from '@/components/ui/dialog';
import {
    Field,
    FieldError,
    FieldGroup,
    FieldLabel,
} from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import {
    EMAIL_MAX,
    EMAIL_REGEX,
    NAME_MAX,
    PASSWORD_LOWER_REGEX,
    PASSWORD_MAX,
    PASSWORD_MIN,
    PASSWORD_NUMBER_REGEX,
    PASSWORD_SPECIAL_REGEX,
    PASSWORD_UPPER_REGEX,
} from '@/lib/constants';
import { route } from '@/lib/route';

type AuthUser = {
    first_name: string;
    last_name: string;
    email: string;
    is_super_admin?: boolean;
    is_sub_admin?: boolean;
};

function profileClientErrors(data: Record<string, unknown>): Record<string, string> {
    const errors: Record<string, string> = {};
    const firstName = String(data.first_name ?? '').trim();
    const lastName = String(data.last_name ?? '').trim();
    const email = String(data.email ?? '').trim();

    if (!firstName) {
        errors.first_name = 'The first name field is required.';
    } else if (firstName.length > NAME_MAX) {
        errors.first_name = `The first name field must not be greater than ${NAME_MAX} characters.`;
    }

    if (!lastName) {
        errors.last_name = 'The last name field is required.';
    } else if (lastName.length > NAME_MAX) {
        errors.last_name = `The last name field must not be greater than ${NAME_MAX} characters.`;
    }

    if (!email) {
        errors.email = 'The email field is required.';
    } else if (email.length > EMAIL_MAX) {
        errors.email = `The email field must not be greater than ${EMAIL_MAX} characters.`;
    } else if (!EMAIL_REGEX.test(email)) {
        errors.email = 'The email field must be a valid email address.';
    }

    return errors;
}

function changePasswordClientErrors(data: Record<string, unknown>): Record<string, string> {
    const errors: Record<string, string> = {};
    const password = String(data.password ?? '');
    const confirmation = String(data.password_confirmation ?? '');

    if (!password) {
        errors.password = 'The password field is required.';
    } else if (password.length < PASSWORD_MIN) {
        errors.password = `The password field must be at least ${PASSWORD_MIN} characters.`;
    } else if (password.length > PASSWORD_MAX) {
        errors.password = `The password field must not be greater than ${PASSWORD_MAX} characters.`;
    } else if (
        !PASSWORD_UPPER_REGEX.test(password) ||
        !PASSWORD_LOWER_REGEX.test(password) ||
        !PASSWORD_NUMBER_REGEX.test(password) ||
        !PASSWORD_SPECIAL_REGEX.test(password)
    ) {
        errors.password =
            'The password must include uppercase, lowercase, number, and special character.';
    }

    if (!confirmation) {
        errors.password_confirmation = 'The password confirmation field is required.';
    } else if (password && confirmation !== password) {
        errors.password_confirmation = 'The password field confirmation does not match.';
    }

    return errors;
}

function PasswordField({
    id,
    name,
    label,
    autoComplete,
    autoFocus,
    visible,
    onToggleVisible,
    error,
    onInput,
}: {
    id: string;
    name: string;
    label: string;
    autoComplete: string;
    autoFocus?: boolean;
    visible: boolean;
    onToggleVisible: () => void;
    error?: string;
    onInput: () => void;
}) {
    return (
        <Field data-invalid={!!error || undefined}>
            <FieldLabel
                htmlFor={id}
                className="text-xs font-medium uppercase tracking-wider text-muted-foreground"
            >
                {label}{' '}
                <span className="text-destructive" aria-hidden>
                    *
                </span>
            </FieldLabel>
            <div className="relative">
                <Input
                    id={id}
                    name={name}
                    type={visible ? 'text' : 'password'}
                    autoComplete={autoComplete}
                    autoFocus={autoFocus}
                    maxLength={PASSWORD_MAX}
                    placeholder="••••••••"
                    aria-invalid={!!error || undefined}
                    onInput={onInput}
                    className="h-11 border-border/80 bg-muted/40 pr-11 shadow-none outline-none transition-colors focus-visible:border-border/80 focus-visible:bg-background focus-visible:ring-0 aria-invalid:ring-0"
                />
                <button
                    type="button"
                    aria-label={visible ? 'Hide password' : 'Show password'}
                    aria-pressed={visible}
                    onClick={onToggleVisible}
                    className="absolute top-1/2 right-3.5 -translate-y-1/2 cursor-pointer text-muted-foreground/70 transition-colors hover:text-foreground"
                >
                    {visible ? (
                        <EyeOff aria-hidden className="size-4" />
                    ) : (
                        <Eye aria-hidden className="size-4" />
                    )}
                </button>
            </div>
            <FieldError>{error}</FieldError>
        </Field>
    );
}

export default function Profile({
    auth,
}: {
    auth: { user: AuthUser | null };
}) {
    const { flash, auth: pageAuth } = usePage().props as {
        flash?: { success?: string | null; error?: string | null };
        auth: { user: AuthUser | null };
    };

    const currentUser = pageAuth.user ?? auth.user;

    const profileFormRef = useRef<FormComponentRef | null>(null);
    const passwordFormRef = useRef<FormComponentRef | null>(null);
    const [editOpen, setEditOpen] = useState(false);
    const [passwordOpen, setPasswordOpen] = useState(false);
    const [newPasswordVisible, setNewPasswordVisible] = useState(false);
    const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
    const { collapsed: sidebarCollapsed, mobileOpen, toggleSidebar, closeMobile, showOpenIcon } =
        useAppSidebar();

    useEffect(() => {
        if (flash?.success) {
            toast.success(flash.success);
        }
        if (flash?.error) {
            toast.error(flash.error);
        }
    }, [flash?.success, flash?.error]);

    const fullName = currentUser
        ? `${currentUser.first_name} ${currentUser.last_name}`
        : '-';

    const roleLabel = currentUser?.is_super_admin
        ? 'Super Admin'
        : currentUser?.is_sub_admin
          ? 'Sub Admin'
          : 'User';

    return (
        <>
            <Head title="Profile" />
            <Toaster
                position="bottom-right"
                toastOptions={{
                    classNames: {
                        success:
                            '!bg-green-600 !text-white !border-green-700 [&_[data-icon]]:!text-white',
                        error: '!bg-red-600 !text-white !border-red-700 [&_[data-icon]]:!text-white',
                    },
                }}
            />
            <div className="flex min-h-svh gap-3 bg-background p-3">
                <AppSidebar
                    auth={{ user: currentUser }}
                    collapsed={sidebarCollapsed}
                    mobileOpen={mobileOpen}
                    onMobileClose={closeMobile}
                    active="profile"
                />

                <div className="flex min-w-0 flex-1 flex-col gap-3">
                    <header className="flex h-12 shrink-0 items-center justify-between gap-3 rounded-2xl border border-border bg-card px-4 shadow-sm">
                        <div className="flex min-w-0 items-center gap-3">
                            <Button
                                type="button"
                                variant="ghost"
                                size="icon"
                                className="cursor-pointer"
                                aria-label={showOpenIcon ? 'Open sidebar' : 'Close sidebar'}
                                onClick={toggleSidebar}
                            >
                                {showOpenIcon ? (
                                    <PanelLeftOpen className="size-4" aria-hidden />
                                ) : (
                                    <PanelLeftClose className="size-4" aria-hidden />
                                )}
                            </Button>

                            <div className="min-w-0">
                                <div className="truncate text-sm font-semibold">Profile</div>
                            </div>
                        </div>

                        <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            className="h-8 gap-1.5 text-xs"
                            onClick={() => router.post(route('logout'))}
                        >
                            <LogOut className="size-4" aria-hidden />
                            Sign out
                        </Button>
                    </header>

                    <main className="flex flex-1 flex-col gap-4 overflow-hidden rounded-2xl border border-border bg-card px-4 py-4 shadow-sm sm:px-5 sm:py-5">
                        <section className="overflow-hidden rounded-xl border border-border bg-background">
                            <div className="flex items-center justify-between gap-3 border-b border-border/60 px-4 py-3">
                                <div className="min-w-0">
                                    <h2 className="text-sm font-semibold tracking-tight">
                                        Account details
                                    </h2>
                                    <p className="mt-0.5 text-xs text-muted-foreground">
                                        These details appear across the dashboard and login.
                                    </p>
                                </div>
                                <Button
                                    type="button"
                                    size="sm"
                                    className="h-8 shrink-0 cursor-pointer gap-1.5"
                                    onClick={() => setEditOpen(true)}
                                    disabled={!currentUser}
                                >
                                    <Pencil className="size-3.5" aria-hidden />
                                    Edit
                                </Button>
                            </div>

                            <dl className="divide-y divide-border/60">
                                <div className="grid grid-cols-[7.5rem_minmax(0,1fr)] items-center gap-3 px-4 py-3 sm:grid-cols-[9rem_minmax(0,1fr)]">
                                    <dt className="flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground">
                                        <UserRound className="size-3.5 shrink-0" aria-hidden />
                                        Name
                                    </dt>
                                    <dd className="min-w-0 truncate text-sm font-medium">
                                        {fullName}
                                    </dd>
                                </div>
                                <div className="grid grid-cols-[7.5rem_minmax(0,1fr)] items-center gap-3 px-4 py-3 sm:grid-cols-[9rem_minmax(0,1fr)]">
                                    <dt className="flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground">
                                        <Mail className="size-3.5 shrink-0" aria-hidden />
                                        Email
                                    </dt>
                                    <dd className="min-w-0 truncate text-sm font-medium">
                                        {currentUser?.email ?? '-'}
                                    </dd>
                                </div>
                                <div className="grid grid-cols-[7.5rem_minmax(0,1fr)] items-center gap-3 px-4 py-3 sm:grid-cols-[9rem_minmax(0,1fr)]">
                                    <dt className="flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground">
                                        <Shield className="size-3.5 shrink-0" aria-hidden />
                                        Role
                                    </dt>
                                    <dd className="min-w-0 truncate text-sm font-medium">
                                        {roleLabel}
                                    </dd>
                                </div>
                            </dl>
                        </section>

                        <section className="overflow-hidden rounded-xl border border-border bg-background">
                            <div className="border-b border-border/60 px-4 py-3">
                                <h2 className="text-sm font-semibold tracking-tight">Security</h2>
                                <p className="mt-0.5 text-xs text-muted-foreground">
                                    Manage how you sign in to SuperGame.
                                </p>
                            </div>

                            <div className="flex items-center gap-3 px-4 py-3">
                                <div className="flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
                                    <KeyRound className="size-3.5" aria-hidden />
                                </div>
                                <div className="min-w-0 flex-1">
                                    <p className="text-sm font-medium">Password</p>
                                    <p className="mt-0.5 text-xs text-muted-foreground">
                                        Change your password to keep the account secure.
                                    </p>
                                </div>
                                <Button
                                    type="button"
                                    variant="outline"
                                    size="sm"
                                    className="h-8 shrink-0 cursor-pointer"
                                    onClick={() => setPasswordOpen(true)}
                                    disabled={!currentUser}
                                >
                                    Change
                                </Button>
                            </div>
                        </section>
                    </main>
                </div>
            </div>

            <Dialog
                open={editOpen}
                onOpenChange={(next) => {
                    setEditOpen(next);
                }}
            >
                <DialogContent
                    className="gap-0 overflow-hidden p-0 sm:max-w-lg"
                    showCloseButton
                >
                    <DialogHeader className="-mx-0 rounded-t-xl border-b bg-muted/50 p-4 pr-12">
                        <DialogTitle className="text-lg font-semibold">Edit Profile</DialogTitle>
                        <DialogDescription>Update your name and email.</DialogDescription>
                    </DialogHeader>

                    {currentUser && (
                        <Form
                            key={`profile-${currentUser.email}-${currentUser.first_name}-${currentUser.last_name}`}
                            ref={profileFormRef}
                            action={route('profile.update')}
                            method="put"
                            noValidate
                            onBefore={() => {
                                const errors = profileClientErrors(
                                    profileFormRef.current?.getData() ?? {},
                                );

                                if (Object.keys(errors).length > 0) {
                                    profileFormRef.current?.setError(errors);

                                    return false;
                                }

                                profileFormRef.current?.clearErrors();
                            }}
                            onSuccess={() => setEditOpen(false)}
                        >
                            {({ errors, processing, clearErrors }) => (
                                <>
                                    <FieldGroup className="gap-5 p-4">
                                        <Field data-invalid={!!errors.first_name || undefined}>
                                            <FieldLabel
                                                htmlFor="first_name"
                                                className="text-xs font-medium uppercase tracking-wider text-muted-foreground"
                                            >
                                                First name{' '}
                                                <span className="text-destructive" aria-hidden>
                                                    *
                                                </span>
                                            </FieldLabel>
                                            <Input
                                                id="first_name"
                                                name="first_name"
                                                defaultValue={currentUser.first_name}
                                                autoComplete="given-name"
                                                autoFocus
                                                maxLength={NAME_MAX}
                                                aria-invalid={!!errors.first_name || undefined}
                                                onInput={() => clearErrors('first_name')}
                                                className="h-11 border-border/80 bg-muted/40 shadow-none outline-none transition-colors focus-visible:border-border/80 focus-visible:bg-background focus-visible:ring-0 aria-invalid:ring-0"
                                            />
                                            <FieldError>{errors.first_name}</FieldError>
                                        </Field>

                                        <Field data-invalid={!!errors.last_name || undefined}>
                                            <FieldLabel
                                                htmlFor="last_name"
                                                className="text-xs font-medium uppercase tracking-wider text-muted-foreground"
                                            >
                                                Last name{' '}
                                                <span className="text-destructive" aria-hidden>
                                                    *
                                                </span>
                                            </FieldLabel>
                                            <Input
                                                id="last_name"
                                                name="last_name"
                                                defaultValue={currentUser.last_name}
                                                autoComplete="family-name"
                                                maxLength={NAME_MAX}
                                                aria-invalid={!!errors.last_name || undefined}
                                                onInput={() => clearErrors('last_name')}
                                                className="h-11 border-border/80 bg-muted/40 shadow-none outline-none transition-colors focus-visible:border-border/80 focus-visible:bg-background focus-visible:ring-0 aria-invalid:ring-0"
                                            />
                                            <FieldError>{errors.last_name}</FieldError>
                                        </Field>

                                        <Field data-invalid={!!errors.email || undefined}>
                                            <FieldLabel
                                                htmlFor="email"
                                                className="text-xs font-medium uppercase tracking-wider text-muted-foreground"
                                            >
                                                Email{' '}
                                                <span className="text-destructive" aria-hidden>
                                                    *
                                                </span>
                                            </FieldLabel>
                                            <Input
                                                id="email"
                                                name="email"
                                                type="email"
                                                defaultValue={currentUser.email}
                                                autoComplete="email"
                                                maxLength={EMAIL_MAX}
                                                aria-invalid={!!errors.email || undefined}
                                                onInput={() => clearErrors('email')}
                                                className="h-11 border-border/80 bg-muted/40 shadow-none outline-none transition-colors focus-visible:border-border/80 focus-visible:bg-background focus-visible:ring-0 aria-invalid:ring-0"
                                            />
                                            <FieldError>{errors.email}</FieldError>
                                        </Field>
                                    </FieldGroup>

                                    <DialogFooter className="mx-0 mb-0">
                                        <Button
                                            type="submit"
                                            disabled={processing}
                                            className="h-11 w-full cursor-pointer sm:w-full"
                                        >
                                            {processing ? 'Saving…' : 'Save Changes'}
                                        </Button>
                                    </DialogFooter>
                                </>
                            )}
                        </Form>
                    )}
                </DialogContent>
            </Dialog>

            <Dialog
                open={passwordOpen}
                onOpenChange={(next) => {
                    setPasswordOpen(next);
                    if (!next) {
                        setNewPasswordVisible(false);
                        setConfirmPasswordVisible(false);
                    }
                }}
            >
                <DialogContent
                    className="gap-0 overflow-hidden p-0 sm:max-w-lg"
                    showCloseButton
                >
                    <DialogHeader className="-mx-0 rounded-t-xl border-b bg-muted/50 p-4 pr-12">
                        <DialogTitle className="text-lg font-semibold">
                            Change Password
                        </DialogTitle>
                        <DialogDescription>
                            Choose a new password for your account.
                        </DialogDescription>
                    </DialogHeader>

                    <Form
                        key={passwordOpen ? 'password-open' : 'password-closed'}
                        ref={passwordFormRef}
                        action={route('profile.password')}
                        method="put"
                        resetOnSuccess={['password', 'password_confirmation']}
                        noValidate
                        onBefore={() => {
                            const errors = changePasswordClientErrors(
                                passwordFormRef.current?.getData() ?? {},
                            );

                            if (Object.keys(errors).length > 0) {
                                passwordFormRef.current?.setError(errors);

                                return false;
                            }

                            passwordFormRef.current?.clearErrors();
                        }}
                        onSuccess={() => {
                            setPasswordOpen(false);
                            setNewPasswordVisible(false);
                            setConfirmPasswordVisible(false);
                        }}
                    >
                        {({ errors, processing, clearErrors }) => (
                            <>
                                <FieldGroup className="gap-5 p-4">
                                    <PasswordField
                                        id="password"
                                        name="password"
                                        label="New password"
                                        autoComplete="new-password"
                                        autoFocus
                                        visible={newPasswordVisible}
                                        onToggleVisible={() =>
                                            setNewPasswordVisible((show) => !show)
                                        }
                                        error={errors.password}
                                        onInput={() => clearErrors('password')}
                                    />

                                    <PasswordField
                                        id="password_confirmation"
                                        name="password_confirmation"
                                        label="Confirm password"
                                        autoComplete="new-password"
                                        visible={confirmPasswordVisible}
                                        onToggleVisible={() =>
                                            setConfirmPasswordVisible((show) => !show)
                                        }
                                        error={errors.password_confirmation}
                                        onInput={() => clearErrors('password_confirmation')}
                                    />

                                    <div
                                        role="note"
                                        className="flex gap-3 rounded-lg border border-amber-300/80 bg-amber-50 px-3.5 py-3 text-sm leading-relaxed text-amber-950"
                                    >
                                        <Info
                                            aria-hidden
                                            className="mt-0.5 size-4 shrink-0 text-amber-600"
                                        />
                                        <p>
                                            Min {PASSWORD_MIN} chars, max {PASSWORD_MAX} chars.
                                            Must include uppercase, lowercase, number, and special
                                            character.
                                        </p>
                                    </div>
                                </FieldGroup>

                                <DialogFooter className="mx-0 mb-0">
                                    <Button
                                        type="submit"
                                        disabled={processing}
                                        className="h-11 w-full cursor-pointer sm:w-full"
                                    >
                                        {processing ? 'Updating…' : 'Update Password'}
                                    </Button>
                                </DialogFooter>
                            </>
                        )}
                    </Form>
                </DialogContent>
            </Dialog>
        </>
    );
}
