import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useParams } from 'react-router-dom';
import { api } from '@/api/client';
import type { AdminEnvironment, AdminPlatform, PlatformList } from '@/api/types';
import { humanMessage } from '@/auth/AuthProvider';
import { ProjectNav } from '@/components/ProjectNav';
import { RequiresAdmin } from '@/components/RequiresAdmin';

const KIND_LABELS: Record<string, string> = {
    web: 'Application web',
    ios: 'iOS',
    android: 'Android',
    backend: 'Back-end',
    email: 'E-mails',
    other: 'Autre',
};

/**
 * Les surfaces d'un projet, et ses cibles de déploiement.
 *
 * Deux notions distinctes réunies sur un écran parce qu'on s'y rend pour la
 * même raison : rendre un projet livrable. Une **plateforme** dit quelles clés
 * entrent dans un fichier et dans quelle syntaxe ; un **environnement** dit
 * quelle version de ce fichier est servie. Il faut les deux, et un projet neuf
 * n'a que le second.
 */
export function PlatformsPage() {
    const { projectUuid = '' } = useParams();

    const stats = useQuery({ queryKey: ['stats', projectUuid], queryFn: () => api.stats(projectUuid) });
    const platforms = useQuery({
        queryKey: ['platforms', projectUuid],
        queryFn: () => api.platforms(projectUuid),
    });
    const environments = useQuery({
        queryKey: ['environments', projectUuid],
        queryFn: () => api.environments(projectUuid),
    });

    if (stats.isSuccess && !stats.data.viewer.canAdminister) {
        return <RequiresAdmin projectUuid={projectUuid} />;
    }

    const active = platforms.data?.platforms.filter((p) => !p.isArchived) ?? [];
    const archived = platforms.data?.platforms.filter((p) => p.isArchived) ?? [];

    return (
        <div className="flex h-full flex-col bg-ink-100">
            <header className="shrink-0 border-b border-ink-200 bg-white px-4 py-2.5">
                <ProjectNav
                    projectUuid={projectUuid}
                    canAdminister={stats.data?.viewer.canAdminister ?? false}
                />
            </header>

            <div className="min-h-0 flex-1 overflow-y-auto">
                <div className="mx-auto max-w-4xl px-6 py-8">
                    <h1 className="text-xl font-semibold tracking-tight text-ink-900">
                        Plateformes et environnements
                    </h1>
                    <p className="mt-1 text-sm text-ink-500">
                        Ce que {stats.data?.projectName} produit, et où cela est servi.
                    </p>

                    {/* Le cas du projet neuf : il a ses environnements mais aucune
                        plateforme, donc rien à livrer. Le dire avant que
                        l'utilisateur ne découvre l'échec au moment de publier. */}
                    {platforms.isSuccess && active.length === 0 && (
                        <p className="mt-5 rounded-md border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900">
                            Aucune plateforme active : ce projet ne peut produire aucun fichier de
                            traduction. Déclarez-en une pour rendre la publication possible.
                        </p>
                    )}

                    <section className="mt-6">
                        <h2 className="mb-3 text-[11px] font-semibold uppercase tracking-wide text-ink-400">
                            Plateformes
                        </h2>

                        <div className="divide-y divide-ink-100 overflow-hidden rounded-lg border border-ink-200 bg-white">
                            {active.map((platform) => (
                                <PlatformRow
                                    key={platform.uuid}
                                    projectUuid={projectUuid}
                                    platform={platform}
                                    options={platforms.data}
                                    isLastActive={active.length === 1}
                                />
                            ))}

                            {active.length === 0 && platforms.isSuccess && (
                                <p className="px-4 py-6 text-center text-sm text-ink-400">
                                    Aucune plateforme active.
                                </p>
                            )}
                        </div>

                        <CreatePlatformForm
                            projectUuid={projectUuid}
                            options={platforms.data}
                            onDone={() => void platforms.refetch()}
                        />
                    </section>

                    {archived.length > 0 && (
                        <section className="mt-8">
                            <h2 className="mb-3 text-[11px] font-semibold uppercase tracking-wide text-ink-400">
                                Archivées
                            </h2>

                            {/* Archivées et non supprimées : les clés leur restent
                                rattachées, et c'est cette trace qui explique
                                pourquoi telle clé existe. */}
                            <div className="divide-y divide-ink-100 overflow-hidden rounded-lg border border-dashed border-ink-300 bg-white opacity-75">
                                {archived.map((platform) => (
                                    <PlatformRow
                                        key={platform.uuid}
                                        projectUuid={projectUuid}
                                        platform={platform}
                                        options={platforms.data}
                                        isLastActive={false}
                                    />
                                ))}
                            </div>
                        </section>
                    )}

                    <section className="mt-10">
                        <h2 className="mb-3 text-[11px] font-semibold uppercase tracking-wide text-ink-400">
                            Environnements
                        </h2>

                        <div className="divide-y divide-ink-100 overflow-hidden rounded-lg border border-ink-200 bg-white">
                            {environments.data?.environments.map((environment) => (
                                <EnvironmentRow
                                    key={environment.uuid}
                                    projectUuid={projectUuid}
                                    environment={environment}
                                />
                            ))}
                        </div>

                        <CreateEnvironmentForm
                            projectUuid={projectUuid}
                            onDone={() => void environments.refetch()}
                        />
                    </section>
                </div>
            </div>
        </div>
    );
}

// ── Plateformes ──────────────────────────────────────────────────────────

function PlatformRow({
    projectUuid,
    platform,
    options,
    isLastActive,
}: {
    projectUuid: string;
    platform: AdminPlatform;
    options: PlatformList | undefined;
    isLastActive: boolean;
}) {
    const queryClient = useQueryClient();
    const [editing, setEditing] = useState(false);
    const [name, setName] = useState(platform.name);
    const [format, setFormat] = useState(platform.messageFormat);
    const [layout, setLayout] = useState(platform.exportLayout);
    const [maxLength, setMaxLength] = useState(platform.defaultMaxLength?.toString() ?? '');
    const [error, setError] = useState<string | null>(null);

    const refresh = () => void queryClient.invalidateQueries({ queryKey: ['platforms', projectUuid] });

    const save = useMutation({
        mutationFn: () =>
            api.updatePlatform(projectUuid, platform.uuid, {
                name,
                messageFormat: format,
                exportLayout: layout,
                defaultMaxLength: maxLength === '' ? null : Number(maxLength),
            }),
        onSuccess: () => {
            setError(null);
            setEditing(false);
            refresh();
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    const toggleArchive = useMutation({
        mutationFn: () =>
            api.updatePlatform(projectUuid, platform.uuid, { isArchived: !platform.isArchived }),
        onSuccess: () => {
            setError(null);
            refresh();
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    if (editing) {
        return (
            <form
                onSubmit={(e) => {
                    e.preventDefault();
                    save.mutate();
                }}
                className="space-y-3 bg-ink-50 px-4 py-4"
            >
                <div className="flex flex-wrap gap-3">
                    <input
                        required
                        value={name}
                        onChange={(e) => setName(e.target.value)}
                        className="min-w-48 flex-1 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                    />

                    {/* Le slug est en lecture seule : il est dans les URL de
                        livraison, dans les tqs.config.json des dépôts clients et
                        dans les scripts d'intégration continue. Le changer
                        casserait tout cela sans qu'aucune erreur ne remonte. */}
                    <span
                        className="rounded-md border border-dashed border-ink-300 bg-ink-100 px-3 py-2 font-mono text-sm text-ink-400"
                        title="Le slug n'est pas modifiable : il apparaît dans les URL de livraison et dans la configuration des dépôts clients."
                    >
                        {platform.slug}
                    </span>

                    <select
                        value={format}
                        onChange={(e) => setFormat(e.target.value)}
                        className="rounded-md border border-ink-300 bg-white px-3 py-2 text-sm outline-none focus:border-accent"
                    >
                        {options?.formats.map((f) => (
                            <option key={f.value} value={f.value}>
                                {f.value} (.{f.fileExtension})
                            </option>
                        ))}
                    </select>

                    <select
                        value={layout}
                        onChange={(e) => setLayout(e.target.value as 'nested' | 'flat')}
                        className="rounded-md border border-ink-300 bg-white px-3 py-2 text-sm outline-none focus:border-accent"
                    >
                        <option value="nested">imbriqué</option>
                        <option value="flat">à plat</option>
                    </select>

                    <input
                        type="number"
                        min={1}
                        value={maxLength}
                        onChange={(e) => setMaxLength(e.target.value)}
                        placeholder="longueur max"
                        className="w-32 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                    />
                </div>

                {error && <p className="text-xs text-red-600">{error}</p>}

                <div className="flex items-center gap-3">
                    <button
                        type="submit"
                        disabled={save.isPending}
                        className="rounded-md bg-accent px-3 py-1.5 text-sm font-medium text-white hover:bg-indigo-700 disabled:bg-ink-300"
                    >
                        Enregistrer
                    </button>
                    <button
                        type="button"
                        onClick={() => {
                            setEditing(false);
                            setError(null);
                        }}
                        className="text-sm text-ink-500 hover:text-ink-800"
                    >
                        Annuler
                    </button>
                </div>
            </form>
        );
    }

    return (
        <div className="px-4 py-3">
            <div className="flex flex-wrap items-center gap-x-3 gap-y-1">
                <span className="text-sm font-medium text-ink-800">{platform.name}</span>
                <span className="font-mono text-[11px] text-ink-400">{platform.slug}</span>
                <span className="rounded bg-ink-100 px-1.5 py-0.5 text-[11px] text-ink-600">
                    {KIND_LABELS[platform.kind] ?? platform.kind}
                </span>

                {!platform.acceptsPush && !platform.isArchived && (
                    <span
                        className="rounded bg-amber-100 px-1.5 py-0.5 text-[11px] text-amber-900"
                        title="Le CLI ne peut pas pousser de clés dans ce format."
                    >
                        push impossible
                    </span>
                )}

                <span className="ml-auto flex shrink-0 items-center gap-2 text-xs">
                    {!platform.isArchived && (
                        <button
                            onClick={() => setEditing(true)}
                            className="rounded border border-ink-300 px-2 py-1 text-ink-600 hover:bg-ink-100"
                        >
                            Modifier
                        </button>
                    )}

                    <button
                        onClick={() => toggleArchive.mutate()}
                        disabled={toggleArchive.isPending || (isLastActive && !platform.isArchived)}
                        title={
                            isLastActive && !platform.isArchived
                                ? 'Dernière plateforme active : l\'archiver rendrait toute publication impossible.'
                                : undefined
                        }
                        className={`rounded border px-2 py-1 transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
                            platform.isArchived
                                ? 'border-ink-300 text-ink-600 hover:bg-ink-100'
                                : 'border-amber-300 text-amber-800 hover:bg-amber-50'
                        }`}
                    >
                        {platform.isArchived ? 'Désarchiver' : 'Archiver'}
                    </button>
                </span>
            </div>

            <div className="mt-1.5 flex flex-wrap gap-x-3 text-[11px] text-ink-400">
                <span className="font-mono">
                    {platform.messageFormat} · {platform.exportLayout === 'nested' ? 'imbriqué' : 'à plat'}
                </span>
                {/* Le chiffre qui rend l'archivage décidable : « 0 clé » se
                    retire sans réfléchir, « 340 clés » demande de vérifier. */}
                <span>
                    {platform.keyCount} clé{platform.keyCount > 1 ? 's' : ''}
                </span>
                {platform.defaultMaxLength !== null && (
                    <span>longueur max {platform.defaultMaxLength}</span>
                )}
                {platform.isArchived && platform.archivedAt !== null && (
                    <span>archivée le {new Date(platform.archivedAt).toLocaleDateString('fr-FR')}</span>
                )}
            </div>

            {error && <p className="mt-2 text-xs text-red-600">{error}</p>}
        </div>
    );
}

function CreatePlatformForm({
    projectUuid,
    options,
    onDone,
}: {
    projectUuid: string;
    options: PlatformList | undefined;
    onDone: () => void;
}) {
    const [open, setOpen] = useState(false);
    const [name, setName] = useState('');
    const [slug, setSlug] = useState('');
    const [kind, setKind] = useState('web');
    const [format, setFormat] = useState('icu');
    const [layout, setLayout] = useState('nested');
    const [maxLength, setMaxLength] = useState('');
    const [error, setError] = useState<string | null>(null);

    const create = useMutation({
        mutationFn: () =>
            api.createPlatform(projectUuid, {
                name,
                slug,
                kind,
                messageFormat: format,
                exportLayout: layout,
                defaultMaxLength: maxLength === '' ? null : Number(maxLength),
            }),
        onSuccess: () => {
            setError(null);
            setName('');
            setSlug('');
            setMaxLength('');
            setOpen(false);
            onDone();
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    if (!open) {
        return (
            <button
                onClick={() => setOpen(true)}
                className="mt-3 rounded-md border border-ink-300 bg-white px-3 py-1.5 text-sm text-ink-700 transition-colors hover:bg-ink-50"
            >
                Ajouter une plateforme
            </button>
        );
    }

    return (
        <form
            onSubmit={(e) => {
                e.preventDefault();
                create.mutate();
            }}
            className="mt-3 rounded-lg border border-ink-200 bg-white p-5 shadow-sm"
        >
            <h3 className="mb-4 text-sm font-medium text-ink-800">Nouvelle plateforme</h3>

            <div className="flex flex-wrap gap-3">
                <input
                    required
                    value={name}
                    onChange={(e) => {
                        setName(e.target.value);
                        setSlug(slugify(e.target.value));
                    }}
                    placeholder="Nom de la plateforme"
                    className="min-w-48 flex-1 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                />

                <input
                    required
                    value={slug}
                    onChange={(e) => setSlug(e.target.value)}
                    placeholder="slug"
                    className="w-40 rounded-md border border-ink-300 px-3 py-2 font-mono text-sm outline-none focus:border-accent"
                />

                <select
                    value={kind}
                    onChange={(e) => {
                        setKind(e.target.value);
                        // Le format suit le type par défaut : une app web se
                        // traduit presque toujours en i18next, une app iOS en
                        // ICU. Le choix reste modifiable juste à côté.
                        const suggested = options?.kinds.find((k) => k.value === e.target.value);
                        if (suggested && options?.formats.some((f) => f.value === suggested.defaultMessageFormat)) {
                            setFormat(suggested.defaultMessageFormat);
                        }
                    }}
                    className="rounded-md border border-ink-300 bg-white px-3 py-2 text-sm outline-none focus:border-accent"
                >
                    {options?.kinds.map((k) => (
                        <option key={k.value} value={k.value}>
                            {KIND_LABELS[k.value] ?? k.value}
                        </option>
                    ))}
                </select>

                <select
                    value={format}
                    onChange={(e) => setFormat(e.target.value)}
                    className="rounded-md border border-ink-300 bg-white px-3 py-2 text-sm outline-none focus:border-accent"
                >
                    {options?.formats.map((f) => (
                        <option key={f.value} value={f.value}>
                            {f.value} (.{f.fileExtension})
                        </option>
                    ))}
                </select>

                <select
                    value={layout}
                    onChange={(e) => setLayout(e.target.value)}
                    className="rounded-md border border-ink-300 bg-white px-3 py-2 text-sm outline-none focus:border-accent"
                >
                    <option value="nested">imbriqué</option>
                    <option value="flat">à plat</option>
                </select>

                <input
                    type="number"
                    min={1}
                    value={maxLength}
                    onChange={(e) => setMaxLength(e.target.value)}
                    placeholder="longueur max"
                    className="w-32 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                />
            </div>

            <p className="mt-3 text-[11px] text-ink-400">
                Seuls les formats disposant d'un sérialiseur sont proposés. Le slug n'est plus
                modifiable ensuite : il apparaît dans les URL de livraison et dans la configuration
                des dépôts clients.
            </p>

            {error && (
                <p className="mt-3 max-h-24 overflow-y-auto rounded bg-red-50 px-3 py-2 text-xs text-red-700">
                    {error}
                </p>
            )}

            <div className="mt-4 flex items-center gap-3">
                <button
                    type="submit"
                    disabled={create.isPending}
                    className="rounded-md bg-accent px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:bg-ink-300"
                >
                    {create.isPending ? 'Création…' : 'Créer la plateforme'}
                </button>
                <button
                    type="button"
                    onClick={() => setOpen(false)}
                    className="text-sm text-ink-500 hover:text-ink-800"
                >
                    Annuler
                </button>
            </div>
        </form>
    );
}

// ── Environnements ───────────────────────────────────────────────────────

function EnvironmentRow({
    projectUuid,
    environment,
}: {
    projectUuid: string;
    environment: AdminEnvironment;
}) {
    const queryClient = useQueryClient();
    const [editing, setEditing] = useState(false);
    const [name, setName] = useState(environment.name);
    const [error, setError] = useState<string | null>(null);

    const save = useMutation({
        mutationFn: () => api.updateEnvironment(projectUuid, environment.uuid, { name }),
        onSuccess: () => {
            setError(null);
            setEditing(false);
            void queryClient.invalidateQueries({ queryKey: ['environments', projectUuid] });
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    return (
        <div className="px-4 py-3">
            <div className="flex flex-wrap items-center gap-x-3 gap-y-1">
                {editing ? (
                    <input
                        autoFocus
                        value={name}
                        onChange={(e) => setName(e.target.value)}
                        onBlur={() => save.mutate()}
                        onKeyDown={(e) => {
                            if (e.key === 'Enter') save.mutate();
                            if (e.key === 'Escape') {
                                setName(environment.name);
                                setEditing(false);
                            }
                        }}
                        className="rounded border border-accent px-2 py-1 text-sm outline-none"
                    />
                ) : (
                    <button
                        onClick={() => setEditing(true)}
                        className="text-sm font-medium text-ink-800 hover:text-accent"
                    >
                        {environment.name}
                    </button>
                )}

                <span className="font-mono text-[11px] text-ink-400">{environment.slug}</span>

                {/* Rien de déployé = LA cause d'un bundle introuvable côté
                    client. La voir ici évite de la chercher ailleurs. */}
                {environment.currentRelease === null ? (
                    <span className="rounded bg-amber-100 px-1.5 py-0.5 text-[11px] text-amber-900">
                        rien de déployé
                    </span>
                ) : (
                    <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[11px] font-medium text-emerald-800">
                        {environment.currentRelease.label}
                    </span>
                )}

                <span className="ml-auto text-[11px] text-ink-400">
                    {environment.activeKeyCount} clé{environment.activeKeyCount > 1 ? 's' : ''} d'accès
                    {environment.totalKeyCount > environment.activeKeyCount &&
                        ` (${environment.totalKeyCount - environment.activeKeyCount} révoquée${
                            environment.totalKeyCount - environment.activeKeyCount > 1 ? 's' : ''
                        })`}
                </span>
            </div>

            {environment.currentRelease !== null && (
                <p className="mt-1.5 text-[11px] text-ink-400">
                    {environment.currentRelease.keyCount} clés · déployée le{' '}
                    {environment.currentRelease.publishedAt !== null
                        ? new Date(environment.currentRelease.publishedAt).toLocaleDateString('fr-FR')
                        : '—'}
                </p>
            )}

            {error && <p className="mt-2 text-xs text-red-600">{error}</p>}
        </div>
    );
}

function CreateEnvironmentForm({
    projectUuid,
    onDone,
}: {
    projectUuid: string;
    onDone: () => void;
}) {
    const [open, setOpen] = useState(false);
    const [name, setName] = useState('');
    const [slug, setSlug] = useState('');
    const [error, setError] = useState<string | null>(null);

    const create = useMutation({
        mutationFn: () => api.createEnvironment(projectUuid, { name, slug }),
        onSuccess: () => {
            setError(null);
            setName('');
            setSlug('');
            setOpen(false);
            onDone();
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    if (!open) {
        return (
            <div className="mt-3">
                <button
                    onClick={() => setOpen(true)}
                    className="rounded-md border border-ink-300 bg-white px-3 py-1.5 text-sm text-ink-700 transition-colors hover:bg-ink-50"
                >
                    Ajouter un environnement
                </button>
                {/* Dit une fois, ici, plutôt que découvert au moment où l'on
                    cherche le bouton qui n'existe pas. */}
                <p className="mt-2 text-[11px] text-ink-400">
                    Un environnement ne se supprime pas : les applications qui l'interrogent
                    cesseraient d'être servies, sans qu'aucun signal ne parte d'ici.
                </p>
            </div>
        );
    }

    return (
        <form
            onSubmit={(e) => {
                e.preventDefault();
                create.mutate();
            }}
            className="mt-3 rounded-lg border border-ink-200 bg-white p-5 shadow-sm"
        >
            <h3 className="mb-4 text-sm font-medium text-ink-800">Nouvel environnement</h3>

            <div className="flex flex-wrap gap-3">
                <input
                    required
                    value={name}
                    onChange={(e) => {
                        setName(e.target.value);
                        setSlug(slugify(e.target.value));
                    }}
                    placeholder="Nom (Recette client, Préproduction…)"
                    className="min-w-56 flex-1 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                />
                <input
                    required
                    value={slug}
                    onChange={(e) => setSlug(e.target.value)}
                    placeholder="slug"
                    className="w-40 rounded-md border border-ink-300 px-3 py-2 font-mono text-sm outline-none focus:border-accent"
                />
            </div>

            <p className="mt-3 text-[11px] text-ink-400">
                Le slug entre dans l'URL de livraison :{' '}
                <span className="font-mono">/delivery/v1/&#123;projet&#125;/{slug || 'slug'}/…</span>{' '}
                — il n'est plus modifiable ensuite.
            </p>

            {error && (
                <p className="mt-3 max-h-24 overflow-y-auto rounded bg-red-50 px-3 py-2 text-xs text-red-700">
                    {error}
                </p>
            )}

            <div className="mt-4 flex items-center gap-3">
                <button
                    type="submit"
                    disabled={create.isPending}
                    className="rounded-md bg-accent px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:bg-ink-300"
                >
                    {create.isPending ? 'Création…' : 'Créer l\'environnement'}
                </button>
                <button
                    type="button"
                    onClick={() => setOpen(false)}
                    className="text-sm text-ink-500 hover:text-ink-800"
                >
                    Annuler
                </button>
            </div>
        </form>
    );
}

function slugify(value: string): string {
    return value
        .toLowerCase()
        .normalize('NFD')
        .replace(/[\u0300-\u036f]/g, '')
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-|-$/g, '');
}
