import { Button, Label, Modal } from '@/Components/ui';
import { factorCaption, factorLabel } from '@/editor/factorLabels';
import { LocalePicker } from '@/Pages/Projects/Index';
import type { LocaleDto } from '@/types/inertia';
import type { FormFactor } from '@/types/layout';
import { router } from '@inertiajs/react';
import { Megaphone, RectangleHorizontal, RectangleVertical } from 'lucide-react';
import { useState, type ComponentType, type SubmitEvent as ReactSubmitEvent, type SVGProps } from 'react';

/**
 * Editor-side project settings modal. Lets the user edit name, active form
 * factors, and locales mid-edit without having to leave the editor.
 *
 * Form-factor edits are destructive on remove (the per-factor layer data is
 * wiped for any removed factor) — we surface a `window.confirm` before
 * dropping a factor that the project already shipped on the server. Adds
 * stamp the primary's content into every screen, no confirmation needed.
 *
 * Brand colors are intentionally NOT editable here. They only seeded the
 * scaffolded background + headline color at create time; editing them
 * later wouldn't recolor existing layouts and would just be a confusing
 * no-op. Per-layer color editing happens in the properties panel.
 */

const PORTRAIT_FACTORS: FormFactor[] = [
    'iphone_portrait',
    'ipad_portrait',
    'android_phone_portrait',
    'android_10_tablet_portrait',
];

const LANDSCAPE_FACTORS: FormFactor[] = [
    'iphone_landscape',
    'ipad_landscape',
    'android_phone_landscape',
    'android_10_tablet_landscape',
];

const MARKETING_FACTORS: FormFactor[] = ['playstore_feature_graphic'];

export default function ProjectSettingsModal({
    project,
    locales,
    onClose,
}: {
    project: {
        id: number;
        name: string;
        form_factor: FormFactor;
        active_form_factors: FormFactor[];
        /** True when the project was created without a kit. Drives the
         *  Sync new layers toggle's visibility — kit-applied projects
         *  don't see it. */
        is_blank?: boolean;
        sync_new_layers?: boolean;
        default_locale: string;
        active_locales: string[];
    };
    locales: LocaleDto[];
    onClose: () => void;
}) {
    const [name, setName] = useState(project.name);
    // Snapshot the server-side factor set on mount. We need this to decide
    // when a remove warrants the destructive-edit confirmation: dropping a
    // factor the project already shipped on the server wipes layer data,
    // but dropping one the user only just added in this session is free.
    const [originalFactors] = useState<Set<FormFactor>>(
        () => new Set(project.active_form_factors),
    );
    const [activeFactors, setActiveFactors] = useState<FormFactor[]>(
        project.active_form_factors,
    );
    const [activeLocales, setActiveLocales] = useState<string[]>(project.active_locales);
    const [defaultLocale, setDefaultLocale] = useState<string>(project.default_locale);
    const [syncNewLayers, setSyncNewLayers] = useState<boolean>(
        project.sync_new_layers ?? false,
    );
    const [submitting, setSubmitting] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const toggleLocale = (code: string) => {
        const has = activeLocales.includes(code);
        if (has) {
            const next = activeLocales.filter((c) => c !== code);
            if (next.length === 0) return;
            setActiveLocales(next);
            if (code === defaultLocale) setDefaultLocale(next[0]);
        } else {
            setActiveLocales([...activeLocales, code]);
        }
    };

    const makeDefault = (code: string) => {
        if (!activeLocales.includes(code)) return;
        setDefaultLocale(code);
    };

    /**
     * Toggle a factor's membership in the active set. Primary stays at
     * index 0 by convention — when the current primary is removed, the
     * next remaining factor is promoted. Adds append (primary doesn't
     * change just by adding). Removal of a previously-saved factor goes
     * through `window.confirm` since the server wipes per-factor layer
     * data for any factor that leaves the set.
     */
    const toggleFactor = (factor: FormFactor) => {
        const checked = activeFactors.includes(factor);
        if (checked) {
            if (activeFactors.length <= 1) return;
            if (originalFactors.has(factor)) {
                const label = factorLabel(factor);
                const ok = window.confirm(
                    `Removing ${label} will discard any ${label}-specific layout edits. Continue?`,
                );
                if (!ok) return;
            }
            setActiveFactors(activeFactors.filter((f) => f !== factor));
        } else {
            setActiveFactors([...activeFactors, factor]);
        }
    };

    const submit = (e: ReactSubmitEvent) => {
        e.preventDefault();
        const trimmed = name.trim();
        if (!trimmed) {
            setError('Name is required.');
            return;
        }
        if (activeFactors.length === 0) {
            setError('At least one form factor is required.');
            return;
        }
        setSubmitting(true);
        setError(null);
        router.patch(
            `/projects/${project.id}`,
            {
                name: trimmed,
                default_locale: defaultLocale,
                active_locales: activeLocales,
                active_form_factors: activeFactors,
                form_factor: activeFactors[0],
                // sync_new_layers is editable on every project (blank +
                // kit-based) — the earlier blank-only gate was removed.
                sync_new_layers: syncNewLayers,
            },
            {
                preserveScroll: true,
                // After save, reload Editor so the active factor + locale sets
                // flow through to the toolbar switcher and the resolver chain.
                // Without this, the store keeps its previous values until
                // next visit.
                onSuccess: () => {
                    onClose();
                    router.reload();
                },
                onError: (errs) => {
                    setError(
                        errs.name ??
                            errs.default_locale ??
                            errs.active_locales ??
                            errs.active_form_factors ??
                            errs.form_factor ??
                            'Update failed.',
                    );
                },
                onFinish: () => setSubmitting(false),
            },
        );
    };

    return (
        <Modal
            open
            onClose={onClose}
            onSubmit={submit}
            size="md"
            data-testid="project-settings-modal"
        >
            <Modal.Header
                eyebrow="Project settings"
                title={project.name}
                subtitle="Rename the project, change which form factors it ships, or change which locales it ships."
            />
            <Modal.Body>
                <div className="space-y-1.5">
                    <Label htmlFor="settings-name">Project name</Label>
                    <input
                        id="settings-name"
                        type="text"
                        value={name}
                        onChange={(e) => setName(e.target.value)}
                        className="block w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-foreground focus:border-accent focus:outline-none"
                        data-testid="settings-name-input"
                    />
                </div>

                <FormFactorPicker
                    activeFactors={activeFactors}
                    onToggle={toggleFactor}
                />

                <label
                    className="flex cursor-pointer items-start gap-3 rounded-md border border-border px-3 py-2 text-[11px] transition-colors hover:bg-muted/40"
                    data-testid="settings-sync-new-layers"
                >
                    <input
                        type="checkbox"
                        className="mt-0.5 h-3.5 w-3.5 shrink-0 accent-accent"
                        checked={syncNewLayers}
                        onChange={(e) => setSyncNewLayers(e.target.checked)}
                        aria-checked={syncNewLayers}
                    />
                    <span className="flex min-w-0 flex-col gap-0.5">
                        <span className="font-medium text-foreground">
                            Sync new layers across devices
                        </span>
                        <span className="text-[10px] font-normal leading-snug text-muted-foreground/80">
                            New text, shape, background, group, or frame
                            layers you add will also appear on the other
                            device factors with axis-scaled positions.
                            Images + callouts stay per-device. Position is
                            always per-factor — only the layer itself
                            propagates.
                        </span>
                        <span className="mt-1 rounded bg-muted/60 px-1.5 py-1 text-[10px] font-normal leading-snug text-foreground/80">
                            <span className="font-medium">Need different text per device?</span>{' '}
                            Select the text layer → Properties → click the
                            <span className="font-mono"> 🔗 Linked</span> button
                            next to “Content” to switch that single layer to{' '}
                            <span className="font-mono">Per-variant</span>.
                            Each device then keeps its own copy.
                        </span>
                    </span>
                </label>

                <LocalePicker
                    locales={locales}
                    activeCodes={activeLocales}
                    defaultCode={defaultLocale}
                    onToggle={toggleLocale}
                    onSetDefault={makeDefault}
                />

                {error && (
                    <p className="text-xs text-destructive" data-testid="settings-error">
                        {error}
                    </p>
                )}
            </Modal.Body>
            <Modal.Footer>
                <Button type="button" variant="ghost" onClick={onClose}>
                    Cancel
                </Button>
                <Button
                    type="submit"
                    disabled={submitting || !name.trim() || activeFactors.length === 0}
                    data-testid="settings-submit"
                >
                    {submitting ? 'Saving…' : 'Save'}
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

/**
 * Mirrors the create-modal `DevicesPicker` visual (Portrait | Landscape grid
 * + Marketing section, pill cards with size-class label + caption + last-pill
 * guard). Deliberately duplicated rather than imported — the create-modal
 * components are tightly coupled to the create form's `useForm` state.
 */
function FormFactorPicker({
    activeFactors,
    onToggle,
}: {
    activeFactors: FormFactor[];
    onToggle: (factor: FormFactor) => void;
}) {
    const activeSet = new Set(activeFactors);
    return (
        <div className="space-y-2">
            <Label>Active form factors</Label>

            <div className="grid grid-cols-2 gap-3 rounded-md border border-border bg-muted/10 p-3">
                <FormFactorColumn
                    heading="Portrait"
                    icon={RectangleVertical}
                    factors={PORTRAIT_FACTORS}
                    activeSet={activeSet}
                    onToggle={onToggle}
                />
                <FormFactorColumn
                    heading="Landscape"
                    icon={RectangleHorizontal}
                    factors={LANDSCAPE_FACTORS}
                    activeSet={activeSet}
                    onToggle={onToggle}
                />
            </div>

            <div className="rounded-md border border-border bg-muted/10 p-3">
                <FormFactorColumn
                    heading="Marketing"
                    icon={Megaphone}
                    factors={MARKETING_FACTORS}
                    activeSet={activeSet}
                    onToggle={onToggle}
                />
            </div>
        </div>
    );
}

function FormFactorColumn({
    heading,
    icon: OrientationIcon,
    factors,
    activeSet,
    onToggle,
}: {
    heading: string;
    icon: ComponentType<SVGProps<SVGSVGElement>>;
    factors: FormFactor[];
    activeSet: Set<FormFactor>;
    onToggle: (factor: FormFactor) => void;
}) {
    return (
        <div className="space-y-1.5">
            <div className="flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70">
                <OrientationIcon className="h-3 w-3" strokeWidth={2} aria-hidden />
                {heading}
            </div>
            <div className="flex flex-col gap-1">
                {factors.map((factor) => {
                    const checked = activeSet.has(factor);
                    // Last-pill guard: refuse to un-toggle the only remaining
                    // selection. The submit button also disables on empty
                    // selection, so the user should never reach a zero state.
                    const canUntoggle = !checked || activeSet.size > 1;
                    const onClick = () => {
                        if (!checked) {
                            onToggle(factor);
                            return;
                        }
                        if (canUntoggle) onToggle(factor);
                    };
                    return (
                        <button
                            key={factor}
                            type="button"
                            onClick={onClick}
                            role="checkbox"
                            aria-checked={checked}
                            data-testid={`settings-factor-${factor}`}
                            data-checked={checked ? 'true' : undefined}
                            title={
                                checked
                                    ? canUntoggle
                                        ? 'Click to remove'
                                        : 'At least one form factor is required.'
                                    : 'Click to add'
                            }
                            className={`flex w-full items-start gap-1.5 rounded-xl border px-3 py-2 text-left text-[11px] font-medium transition-colors ${
                                checked
                                    ? 'border-accent bg-accent/10 text-foreground'
                                    : 'border-border bg-background text-muted-foreground hover:border-border/80 hover:text-foreground'
                            }`}
                        >
                            <span className="flex min-w-0 flex-col gap-0.5">
                                <span className="truncate">{factorLabel(factor)}</span>
                                <span
                                    className="truncate text-[10px] font-normal text-muted-foreground/80"
                                    data-testid={`settings-factor-caption-${factor}`}
                                >
                                    {factorCaption(factor)}
                                </span>
                            </span>
                        </button>
                    );
                })}
            </div>
        </div>
    );
}
