import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useMemo, useState } from 'react';

import { Alert } from '@/components/ui/Alert';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import { Modal } from '@/components/ui/Modal';
import { SectionHeader } from '@/components/ui/SectionHeader';
import { Tag } from '@/components/ui/Tag';
import { TextField } from '@/components/ui/TextField';
import {
  ApiError,
  apiDelete,
  apiGet,
  apiPatch,
  apiPost,
  apiPut,
} from '@/lib/api';
import {
  adminKeys,
  groupPermsByFamily,
  type AdminGroup,
  type AdminGroupListResponse,
  type AdminPermission,
} from '@/lib/admin';

function usePermissions() {
  return useQuery({
    queryKey: adminKeys.permissions,
    queryFn: () => apiGet<{ items: AdminPermission[] }>('/permissions'),
  });
}

function useGroups() {
  return useQuery({
    queryKey: adminKeys.groups,
    queryFn: () => apiGet<AdminGroupListResponse>('/groups'),
  });
}

export function AdminGroupsPage() {
  const groups = useGroups();
  const perms = usePermissions();
  const [editing, setEditing] = useState<AdminGroup | null>(null);
  const [creating, setCreating] = useState(false);

  return (
    <>
      <SectionHeader
        prefix="Admin"
        highlight="Groups"
        accent="purple"
        description="Compose custom groups; combine atomic permissions to express any role."
      />

      <div className="mb-6 flex items-center justify-between">
        <span className="font-mono text-2xs text-text-dim">
          {groups.data ? `${groups.data.total} group${groups.data.total === 1 ? '' : 's'}` : ''}
        </span>
        <Button accent="purple" onClick={() => setCreating(true)} data-testid="create-group">
          + New group
        </Button>
      </div>

      {(groups.isError || perms.isError) && (
        <Alert accent="red">Failed to load groups or permissions.</Alert>
      )}

      <div className="grid gap-3" data-testid="groups-table">
        {(groups.isLoading || perms.isLoading) && (
          <p className="font-mono text-xs text-text-dim">Loading…</p>
        )}
        {groups.data?.items.map((g) => (
          <Card key={g.id} accent={g.is_system ? 'yellow' : 'purple'} title={g.name} sub={g.description ?? '—'}>
            <div className="flex flex-wrap items-center gap-2">
              {g.is_system && <Tag accent="yellow">SYSTEM</Tag>}
              <Tag accent="cyan">{g.members_count} member{g.members_count === 1 ? '' : 's'}</Tag>
              <Tag accent="orange">{g.permissions.length} perm{g.permissions.length === 1 ? '' : 's'}</Tag>
              <div className="ml-auto flex gap-2">
                <Button accent="purple" onClick={() => setEditing(g)} data-testid={`edit-group-${g.name}`}>
                  Edit
                </Button>
              </div>
            </div>
          </Card>
        ))}
        {groups.data && groups.data.items.length === 0 && (
          <p className="font-mono text-xs text-text-dim">No groups yet.</p>
        )}
      </div>

      {creating && perms.data && (
        <GroupCreateModal allPerms={perms.data.items} onClose={() => setCreating(false)} />
      )}
      {editing && perms.data && (
        <GroupEditModal
          group={editing}
          allPerms={perms.data.items}
          onClose={() => setEditing(null)}
        />
      )}
    </>
  );
}

interface PermsMultiSelectProps {
  allPerms: AdminPermission[];
  selected: Set<string>;
  onToggle: (code: string) => void;
}

function PermsMultiSelect({ allPerms, selected, onToggle }: PermsMultiSelectProps) {
  const byFamily = useMemo(
    () => groupPermsByFamily(allPerms.map((p) => p.code)),
    [allPerms],
  );
  return (
    <div className="max-h-72 overflow-y-auto rounded border border-border bg-bg-card p-3" data-testid="perms-multiselect">
      {Object.entries(byFamily).map(([family, codes]) => (
        <div key={family} className="mb-3 last:mb-0">
          <p className="font-mono text-3xs uppercase tracking-wider2 text-text-dim mb-2">
            {family}
          </p>
          <div className="flex flex-wrap gap-2">
            {codes.map((code) => (
              <label
                key={code}
                className="inline-flex items-center gap-2 rounded border border-border px-2 py-1 cursor-pointer hover:border-cyan"
              >
                <input
                  type="checkbox"
                  checked={selected.has(code)}
                  onChange={() => onToggle(code)}
                  data-testid={`perm-${code}`}
                />
                <span className="font-mono text-2xs">{code}</span>
              </label>
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

function GroupCreateModal({
  allPerms,
  onClose,
}: {
  allPerms: AdminPermission[];
  onClose: () => void;
}) {
  const qc = useQueryClient();
  const [name, setName] = useState('');
  const [description, setDescription] = useState('');
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [error, setError] = useState<string | null>(null);

  const createGroup = useMutation({
    mutationFn: () =>
      apiPost<AdminGroup>('/groups', {
        name: name.trim(),
        description: description.trim() || null,
      }),
  });
  const setPerms = useMutation({
    mutationFn: (groupId: string) =>
      apiPut(`/groups/${groupId}/permissions`, { codes: Array.from(selected) }),
  });

  async function save() {
    setError(null);
    try {
      const g = await createGroup.mutateAsync();
      if (selected.size) await setPerms.mutateAsync(g.id);
      await qc.invalidateQueries({ queryKey: adminKeys.groups });
      onClose();
    } catch (e) {
      if (e instanceof ApiError) {
        const p = e.payload as { error?: string; message?: string } | null;
        setError(p?.message ?? p?.error ?? `HTTP ${e.status}`);
      } else {
        setError(e instanceof Error ? e.message : 'Save failed');
      }
    }
  }

  return (
    <Modal open onClose={onClose} title="new group" accent="purple" testid="group-create-modal">
      <div className="space-y-4">
        <TextField
          label="Name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          hint="Lower-case-with-dashes recommended (e.g. pentest-2026-Q2)"
          required
        />
        <TextField
          label="Description"
          value={description}
          onChange={(e) => setDescription(e.target.value)}
        />
        <div>
          <p className="font-mono text-3xs uppercase tracking-wider2 text-text-dim mb-2">
            Permissions
          </p>
          <PermsMultiSelect
            allPerms={allPerms}
            selected={selected}
            onToggle={(code) =>
              setSelected((prev) => {
                const next = new Set(prev);
                if (next.has(code)) next.delete(code);
                else next.add(code);
                return next;
              })
            }
          />
        </div>
        {error && <Alert accent="red">{error}</Alert>}
        <div className="flex justify-end gap-2 pt-2">
          <Button variant="ghost" onClick={onClose}>
            Cancel
          </Button>
          <Button accent="purple" onClick={save} data-testid="group-create-save">
            Create group
          </Button>
        </div>
      </div>
    </Modal>
  );
}

function GroupEditModal({
  group,
  allPerms,
  onClose,
}: {
  group: AdminGroup;
  allPerms: AdminPermission[];
  onClose: () => void;
}) {
  const qc = useQueryClient();
  const [name, setName] = useState(group.name);
  const [description, setDescription] = useState(group.description ?? '');
  const [selected, setSelected] = useState<Set<string>>(new Set(group.permissions));
  const [error, setError] = useState<string | null>(null);

  const patchGroup = useMutation({
    mutationFn: () =>
      apiPatch(`/groups/${group.id}`, {
        name: group.is_system ? undefined : name.trim(),
        description: description.trim() || null,
      }),
  });
  const setPerms = useMutation({
    mutationFn: () =>
      apiPut(`/groups/${group.id}/permissions`, { codes: Array.from(selected) }),
  });
  const del = useMutation({
    mutationFn: () => apiDelete(`/groups/${group.id}`),
  });

  async function save() {
    setError(null);
    try {
      await patchGroup.mutateAsync();
      await setPerms.mutateAsync();
      await qc.invalidateQueries({ queryKey: adminKeys.groups });
      onClose();
    } catch (e) {
      if (e instanceof ApiError) {
        const p = e.payload as { error?: string; message?: string } | null;
        setError(p?.message ?? p?.error ?? `HTTP ${e.status}`);
      } else {
        setError(e instanceof Error ? e.message : 'Save failed');
      }
    }
  }

  async function handleDelete() {
    if (!confirm(`Soft-delete group "${group.name}"?`)) return;
    try {
      await del.mutateAsync();
      await qc.invalidateQueries({ queryKey: adminKeys.groups });
      onClose();
    } catch (e) {
      if (e instanceof ApiError) {
        const p = e.payload as { error?: string; message?: string } | null;
        setError(p?.message ?? p?.error ?? `HTTP ${e.status}`);
      } else {
        setError(e instanceof Error ? e.message : 'Delete failed');
      }
    }
  }

  return (
    <Modal open onClose={onClose} title={group.name} accent="purple" testid="group-edit-modal">
      <div className="space-y-4">
        <TextField
          label="Name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          disabled={group.is_system}
          hint={group.is_system ? 'System groups cannot be renamed.' : undefined}
        />
        <TextField
          label="Description"
          value={description}
          onChange={(e) => setDescription(e.target.value)}
        />
        <div>
          <p className="font-mono text-3xs uppercase tracking-wider2 text-text-dim mb-2">
            Permissions
          </p>
          <PermsMultiSelect
            allPerms={allPerms}
            selected={selected}
            onToggle={(code) =>
              setSelected((prev) => {
                const next = new Set(prev);
                if (next.has(code)) next.delete(code);
                else next.add(code);
                return next;
              })
            }
          />
        </div>
        {error && <Alert accent="red">{error}</Alert>}
        <div className="flex items-center justify-between gap-3 pt-2">
          {!group.is_system && (
            <Button accent="rose" onClick={handleDelete}>
              Delete group
            </Button>
          )}
          <div className="ml-auto flex gap-2">
            <Button variant="ghost" onClick={onClose}>
              Cancel
            </Button>
            <Button accent="purple" onClick={save} data-testid="group-edit-save">
              Save changes
            </Button>
          </div>
        </div>
      </div>
    </Modal>
  );
}
