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 { Member, ProjectRole } from '@/api/types';
import { humanMessage } from '@/auth/AuthProvider';
import { ProjectNav } from '@/components/ProjectNav';
import { RequiresAdmin } from '@/components/RequiresAdmin';

const ROLE_HELP: Record<ProjectRole, string> = {
    owner: 'Tout, y compris la suppression du projet.',
    admin: 'Membres, langues, plateformes, clés API, publication.',
    developer: 'Clés, plateformes, releases. N\'écrit PAS de traductions.',
    translator: 'Écrit les traductions de ses langues assignées.',
    reviewer: 'Comme traducteur, plus la validation.',
    viewer: 'Lecture seule.',
};

/**
 * Administration des accès.
 *
 * L'écran est construit autour d'une question : « qui peut écrire quoi ? ».
 * D'où les langues affichées sur chaque ligne plutôt que cachées derrière une
 * fiche — c'est l'information qui décide si un prestataire est correctement
 * cloisonné, et elle doit se vérifier d'un coup d'œil.
 */
export function MembersPage() {
    const { projectUuid = '' } = useParams();
    const queryClient = useQueryClient();

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

    const targetLocales = (stats.data?.locales ?? []).filter((l) => !l.isSource);

    // Le serveur refuse déjà ; ici on refuse LISIBLEMENT.
    if (stats.isSuccess && !stats.data.viewer.canAdminister) {
        return <RequiresAdmin projectUuid={projectUuid} />;
    }

    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">Membres</h1>
                    <p className="mt-1 text-sm text-ink-500">
                        Qui a accès à {stats.data?.projectName}, et sur quelles langues.
                    </p>

                    <InviteForm
                        projectUuid={projectUuid}
                        locales={targetLocales.map((l) => l.code)}
                        onDone={() => void queryClient.invalidateQueries({ queryKey: ['members', projectUuid] })}
                    />

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

                        <div className="divide-y divide-ink-100 overflow-hidden rounded-lg border border-ink-200 bg-white">
                            {data.data?.members.map((member) => (
                                <MemberRow
                                    key={member.uuid}
                                    projectUuid={projectUuid}
                                    member={member}
                                    locales={targetLocales.map((l) => l.code)}
                                />
                            ))}
                        </div>
                    </section>

                    {(data.data?.pending.length ?? 0) > 0 && (
                        <section className="mt-8">
                            <h2 className="mb-3 text-[11px] font-semibold uppercase tracking-wide text-ink-400">
                                Invitations en attente
                            </h2>

                            {/* Les invitations non acceptées comptent comme des accès
                                en cours d'octroi : les séparer dans un autre écran
                                laisserait un angle mort à qui audite les accès. */}
                            <div className="divide-y divide-ink-100 overflow-hidden rounded-lg border border-dashed border-ink-300 bg-white">
                                {data.data?.pending.map((invitation) => (
                                    <div key={invitation.email} className="flex items-center gap-3 px-4 py-3">
                                        <span className="flex-1 truncate text-sm text-ink-700">
                                            {invitation.email}
                                        </span>
                                        <span className="rounded bg-ink-100 px-1.5 py-0.5 text-[11px] text-ink-600">
                                            {invitation.role}
                                        </span>
                                        <span className="font-mono text-[11px] text-ink-400">
                                            {invitation.locales.length === 0
                                                ? 'toutes langues'
                                                : invitation.locales.join(', ')}
                                        </span>
                                        <span className="text-[11px] text-ink-400">
                                            expire le{' '}
                                            {new Date(invitation.expiresAt).toLocaleDateString('fr-FR')}
                                        </span>
                                    </div>
                                ))}
                            </div>
                        </section>
                    )}
                </div>
            </div>
        </div>
    );
}

function InviteForm({
    projectUuid,
    locales,
    onDone,
}: {
    projectUuid: string;
    locales: string[];
    onDone: () => void;
}) {
    const [email, setEmail] = useState('');
    const [role, setRole] = useState<ProjectRole>('translator');
    const [selected, setSelected] = useState<string[]>([]);
    const [isExternal, setIsExternal] = useState(false);
    const [message, setMessage] = useState<string | null>(null);
    const [error, setError] = useState<string | null>(null);

    const invite = useMutation({
        mutationFn: () => api.invite(projectUuid, { email, role, locales: selected, isExternal }),
        onSuccess: (result) => {
            setMessage(result.message);
            setError(null);
            setEmail('');
            setSelected([]);
            onDone();
        },
        onError: (caught) => {
            setError(humanMessage(caught));
            setMessage(null);
        },
    });

    // Les langues ne concernent que les rôles qui écrivent : les proposer à un
    // developer suggérerait une restriction qui n'existe pas pour lui.
    const showLocales = role === 'translator' || role === 'reviewer';

    return (
        <form
            onSubmit={(e) => {
                e.preventDefault();
                invite.mutate();
            }}
            className="mt-6 rounded-lg border border-ink-200 bg-white p-5 shadow-sm"
        >
            <h2 className="mb-4 text-sm font-medium text-ink-800">Inviter quelqu'un</h2>

            <div className="flex flex-wrap gap-3">
                <input
                    type="email"
                    required
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                    placeholder="adresse@exemple.fr"
                    className="min-w-56 flex-1 rounded-md border border-ink-300 px-3 py-2 text-sm outline-none focus:border-accent"
                />

                <select
                    value={role}
                    onChange={(e) => setRole(e.target.value as ProjectRole)}
                    className="rounded-md border border-ink-300 bg-white px-2 py-2 text-sm outline-none focus:border-accent"
                >
                    {(Object.keys(ROLE_HELP) as ProjectRole[]).map((value) => (
                        <option key={value} value={value}>
                            {value}
                        </option>
                    ))}
                </select>

                <button
                    type="submit"
                    disabled={invite.isPending}
                    className="rounded-md bg-accent px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:opacity-60"
                >
                    {invite.isPending ? 'Envoi…' : 'Inviter'}
                </button>
            </div>

            <p className="mt-2 text-xs text-ink-400">{ROLE_HELP[role]}</p>

            {showLocales && (
                <div className="mt-4">
                    <p className="mb-1.5 text-xs font-medium text-ink-600">
                        Langues autorisées{' '}
                        <span className="font-normal text-ink-400">
                            — aucune sélection = toutes les langues du projet
                        </span>
                    </p>
                    <div className="flex flex-wrap gap-1.5">
                        {locales.map((code) => {
                            const on = selected.includes(code);

                            return (
                                <button
                                    key={code}
                                    type="button"
                                    onClick={() =>
                                        setSelected((prev) =>
                                            on ? prev.filter((c) => c !== code) : [...prev, code],
                                        )
                                    }
                                    className={`rounded px-2 py-1 font-mono text-xs transition-colors ${
                                        on ? 'bg-accent text-white' : 'bg-ink-100 text-ink-600 hover:bg-ink-200'
                                    }`}
                                >
                                    {code}
                                </button>
                            );
                        })}
                    </div>
                </div>
            )}

            <label className="mt-4 flex items-center gap-2 text-xs text-ink-600">
                <input
                    type="checkbox"
                    checked={isExternal}
                    onChange={(e) => setIsExternal(e.target.checked)}
                />
                Prestataire externe
                <span className="text-ink-400">
                    — fenêtre d'invitation raccourcie et connexions journalisées
                </span>
            </label>

            {message !== null && (
                <p className="mt-4 rounded-md bg-emerald-50 px-3 py-2 text-sm text-emerald-800">{message}</p>
            )}
            {error !== null && (
                <p role="alert" className="mt-4 rounded-md bg-red-50 px-3 py-2 text-sm text-red-700">
                    {error}
                </p>
            )}
        </form>
    );
}

function MemberRow({
    projectUuid,
    member,
    locales,
}: {
    projectUuid: string;
    member: Member;
    locales: string[];
}) {
    const queryClient = useQueryClient();
    const [editing, setEditing] = useState(false);
    const [error, setError] = useState<string | null>(null);

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

    const update = useMutation({
        mutationFn: (body: { role?: ProjectRole; locales?: string[] }) =>
            api.updateMember(projectUuid, member.uuid, body),
        onSuccess: () => {
            setError(null);
            setEditing(false);
            refresh();
        },
        onError: (caught) => setError(humanMessage(caught)),
    });

    const remove = useMutation({
        mutationFn: () => api.removeMember(projectUuid, member.uuid),
        onSuccess: refresh,
        onError: (caught) => setError(humanMessage(caught)),
    });

    return (
        <div className="px-4 py-3">
            <div className="flex flex-wrap items-center gap-3">
                <div className="min-w-40 flex-1">
                    <p className="text-sm font-medium text-ink-800">
                        {member.name}
                        {member.isExternal && (
                            <span className="ml-2 rounded bg-ink-200 px-1.5 py-0.5 text-[10px] font-normal text-ink-600">
                                externe
                            </span>
                        )}
                    </p>
                    <p className="text-xs text-ink-400">{member.email}</p>
                </div>

                <select
                    value={member.role}
                    onChange={(e) => update.mutate({ role: e.target.value as ProjectRole })}
                    className="rounded border border-ink-300 bg-white px-2 py-1 text-xs outline-none focus:border-accent"
                >
                    {(Object.keys(ROLE_HELP) as ProjectRole[]).map((value) => (
                        <option key={value} value={value}>
                            {value}
                        </option>
                    ))}
                </select>

                <button
                    onClick={() => setEditing((v) => !v)}
                    className="min-w-32 rounded px-2 py-1 text-left font-mono text-[11px] text-ink-500 hover:bg-ink-100"
                    title="Modifier les langues autorisées"
                >
                    {member.coversAllLocales ? 'toutes langues' : member.locales.join(', ')}
                </button>

                <span className="text-[11px] text-ink-400">
                    {member.lastLoginAt === null
                        ? 'jamais connecté'
                        : new Date(member.lastLoginAt).toLocaleDateString('fr-FR')}
                </span>

                <button
                    onClick={() => remove.mutate()}
                    className="rounded px-2 py-1 text-xs text-ink-400 hover:bg-red-50 hover:text-red-700"
                >
                    Retirer
                </button>
            </div>

            {editing && (
                <div className="mt-3 flex flex-wrap gap-1.5 border-t border-ink-100 pt-3">
                    {locales.map((code) => {
                        const on = member.locales.includes(code);

                        return (
                            <button
                                key={code}
                                onClick={() =>
                                    update.mutate({
                                        locales: on
                                            ? member.locales.filter((c) => c !== code)
                                            : [...member.locales, code],
                                    })
                                }
                                className={`rounded px-2 py-1 font-mono text-xs transition-colors ${
                                    on ? 'bg-accent text-white' : 'bg-ink-100 text-ink-600 hover:bg-ink-200'
                                }`}
                            >
                                {code}
                            </button>
                        );
                    })}
                    <span className="self-center text-xs text-ink-400">
                        aucune sélection = toutes les langues
                    </span>
                </div>
            )}

            {error !== null && (
                <p role="alert" className="mt-2 text-xs text-red-700">
                    {error}
                </p>
            )}
        </div>
    );
}
