import { useEffect, useMemo, useState, ReactNode } from 'react';
import { Head } from '@inertiajs/react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import AdminLayout from '@/layouts/AdminLayout';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Checkbox } from '@/components/ui/checkbox';
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogTrigger,
} from '@/components/ui/dialog';
import {
  AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
  AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
  AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { toast } from 'sonner';
import {
  Shield, Plus, Trash2, Save, Lock, Users, AlertTriangle,
} from 'lucide-react';

type Role = {
  id: string;
  name: string;
  label: string;
  priority: number;
  landing_route: string | null;
  is_system: boolean;
  deletable: boolean;
  page_ids: string[];
  user_count: number;
};

type Page = {
  id: string;
  key: string;
  label: string;
  group: string | null;
  icon: string | null;
  route_names: string[];
  sort_order: number;
};

const csrfToken = () =>
  (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement | null)?.content ?? '';

const apiFetch = async (url: string, init?: RequestInit) => {
  const res = await fetch(url, {
    ...init,
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-TOKEN': csrfToken(),
      'Accept': 'application/json',
      ...(init?.headers ?? {}),
    },
  });
  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(body?.error || `Request failed (${res.status})`);
  return body;
};

const RoleManagementPage = () => {
  const qc = useQueryClient();
  const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
  const [draftRole, setDraftRole] = useState<Partial<Role>>({});
  const [draftPageIds, setDraftPageIds] = useState<Set<string>>(new Set());
  const [createOpen, setCreateOpen] = useState(false);

  const { data: roles = [], isLoading: rolesLoading } = useQuery<Role[]>({
    queryKey: ['admin-roles'],
    queryFn: () => apiFetch('/api/admin/roles'),
  });

  const { data: pages = [] } = useQuery<Page[]>({
    queryKey: ['admin-pages'],
    queryFn: () => apiFetch('/api/admin/pages'),
  });

  const selectedRole = useMemo(
    () => roles.find((r) => r.id === selectedRoleId) ?? null,
    [roles, selectedRoleId],
  );

  // Auto-select the first role on initial load.
  useEffect(() => {
    if (!selectedRoleId && roles.length > 0) {
      setSelectedRoleId(roles[0].id);
    }
  }, [roles, selectedRoleId]);

  // Sync local draft state when the selected role changes (or when the role
  // refetches after a save).
  useEffect(() => {
    if (selectedRole) {
      setDraftRole({
        name: selectedRole.name,
        label: selectedRole.label,
        priority: selectedRole.priority,
        landing_route: selectedRole.landing_route ?? '',
      });
      setDraftPageIds(new Set(selectedRole.page_ids));
    }
  }, [selectedRole]);

  // Group pages by their `group` field for the checkbox grid.
  const pageGroups = useMemo(() => {
    const groups = new Map<string, Page[]>();
    for (const p of pages) {
      const key = p.group ?? 'Hidden / Detail Pages';
      if (!groups.has(key)) groups.set(key, []);
      groups.get(key)!.push(p);
    }
    return Array.from(groups.entries());
  }, [pages]);

  // Mutations
  const updateRoleMut = useMutation({
    mutationFn: () => {
      if (!selectedRole) throw new Error('No role selected');
      const payload: Record<string, unknown> = {
        label: draftRole.label,
        priority: Number(draftRole.priority ?? 0),
        landing_route: draftRole.landing_route?.trim() || null,
      };
      // Only send name if the role's name is editable (i.e. not a system role).
      if (!selectedRole.is_system && draftRole.name && draftRole.name !== selectedRole.name) {
        payload.name = draftRole.name;
      }
      return apiFetch(`/api/admin/roles/${selectedRole.id}`, {
        method: 'PATCH',
        body: JSON.stringify(payload),
      });
    },
    onSuccess: () => {
      toast.success('Role saved');
      qc.invalidateQueries({ queryKey: ['admin-roles'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const syncPagesMut = useMutation({
    mutationFn: () => {
      if (!selectedRole) throw new Error('No role selected');
      return apiFetch(`/api/admin/roles/${selectedRole.id}/pages`, {
        method: 'PUT',
        body: JSON.stringify({ page_ids: Array.from(draftPageIds) }),
      });
    },
    onSuccess: () => {
      toast.success('Permissions saved');
      qc.invalidateQueries({ queryKey: ['admin-roles'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const deleteRoleMut = useMutation({
    mutationFn: (id: string) => apiFetch(`/api/admin/roles/${id}`, { method: 'DELETE' }),
    onSuccess: () => {
      toast.success('Role deleted');
      setSelectedRoleId(null);
      qc.invalidateQueries({ queryKey: ['admin-roles'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const createRoleMut = useMutation({
    mutationFn: (payload: { name: string; label: string; priority: number; landing_route: string | null }) =>
      apiFetch('/api/admin/roles', { method: 'POST', body: JSON.stringify(payload) }),
    onSuccess: (created: Role) => {
      toast.success('Role created');
      setCreateOpen(false);
      setSelectedRoleId(created.id);
      qc.invalidateQueries({ queryKey: ['admin-roles'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const togglePage = (pageId: string) => {
    setDraftPageIds((prev) => {
      const next = new Set(prev);
      next.has(pageId) ? next.delete(pageId) : next.add(pageId);
      return next;
    });
  };

  const toggleGroup = (groupPages: Page[]) => {
    const allChecked = groupPages.every((p) => draftPageIds.has(p.id));
    setDraftPageIds((prev) => {
      const next = new Set(prev);
      if (allChecked) {
        groupPages.forEach((p) => next.delete(p.id));
      } else {
        groupPages.forEach((p) => next.add(p.id));
      }
      return next;
    });
  };

  const pagesDirty = useMemo(() => {
    if (!selectedRole) return false;
    const orig = new Set(selectedRole.page_ids);
    if (orig.size !== draftPageIds.size) return true;
    for (const id of draftPageIds) if (!orig.has(id)) return true;
    return false;
  }, [draftPageIds, selectedRole]);

  const metaDirty = useMemo(() => {
    if (!selectedRole) return false;
    return (
      draftRole.label !== selectedRole.label ||
      Number(draftRole.priority ?? 0) !== selectedRole.priority ||
      (draftRole.landing_route ?? '') !== (selectedRole.landing_route ?? '') ||
      (!selectedRole.is_system && draftRole.name !== selectedRole.name)
    );
  }, [draftRole, selectedRole]);

  return (
    <>
      <Head title="Role Management" />
      <div className="space-y-6">
        <div className="flex items-start justify-between">
          <div>
            <h1 className="text-2xl font-bold flex items-center gap-2">
              <Shield className="h-6 w-6 text-primary" />
              Role Management
            </h1>
            <p className="text-muted-foreground mt-1 text-sm">
              Define roles and choose which pages each one can access. Changes apply immediately.
            </p>
          </div>
          <CreateRoleDialog
            open={createOpen}
            onOpenChange={setCreateOpen}
            onCreate={(payload) => createRoleMut.mutate(payload)}
            isPending={createRoleMut.isPending}
          />
        </div>

        <div className="grid grid-cols-1 lg:grid-cols-[320px_1fr] gap-6">
          {/* Left: roles list */}
          <Card className="lg:sticky lg:top-4 lg:self-start">
            <CardHeader className="pb-3">
              <CardTitle className="text-base">Roles ({roles.length})</CardTitle>
            </CardHeader>
            <CardContent className="space-y-1 p-2">
              {rolesLoading && (
                <p className="text-sm text-muted-foreground py-4 text-center">Loading…</p>
              )}
              {roles.map((r) => {
                const active = r.id === selectedRoleId;
                return (
                  <button
                    key={r.id}
                    onClick={() => setSelectedRoleId(r.id)}
                    className={[
                      'w-full text-left px-3 py-2 rounded-md transition-colors',
                      'flex items-start justify-between gap-2',
                      active ? 'bg-primary/10 border border-primary/30' : 'hover:bg-muted',
                    ].join(' ')}
                  >
                    <div className="min-w-0">
                      <div className="font-medium text-sm truncate flex items-center gap-1.5">
                        {r.label}
                        {r.is_system && (
                          <Lock className="h-3 w-3 text-muted-foreground shrink-0" />
                        )}
                      </div>
                      <div className="text-xs text-muted-foreground truncate">
                        {r.name} · {r.user_count} user{r.user_count === 1 ? '' : 's'}
                      </div>
                    </div>
                    <Badge variant="outline" className="text-xs shrink-0">
                      {r.page_ids.length}
                    </Badge>
                  </button>
                );
              })}
            </CardContent>
          </Card>

          {/* Right: editor */}
          {selectedRole ? (
            <div className="space-y-6">
              {/* Role meta */}
              <Card>
                <CardHeader>
                  <CardTitle className="text-base flex items-center gap-2">
                    Editing: <span className="font-mono text-sm">{selectedRole.name}</span>
                    {selectedRole.is_system && (
                      <Badge variant="secondary" className="text-xs gap-1">
                        <Lock className="h-3 w-3" />
                        system role
                      </Badge>
                    )}
                  </CardTitle>
                </CardHeader>
                <CardContent>
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div className="space-y-2">
                      <Label>Identifier</Label>
                      <Input
                        value={draftRole.name ?? ''}
                        onChange={(e) => setDraftRole((p) => ({ ...p, name: e.target.value }))}
                        disabled={selectedRole.is_system}
                        placeholder="lowercase_with_underscores"
                      />
                      {selectedRole.is_system && (
                        <p className="text-xs text-muted-foreground">
                          System role names are referenced in code and can't be renamed.
                        </p>
                      )}
                    </div>
                    <div className="space-y-2">
                      <Label>Display label</Label>
                      <Input
                        value={draftRole.label ?? ''}
                        onChange={(e) => setDraftRole((p) => ({ ...p, label: e.target.value }))}
                      />
                    </div>
                    <div className="space-y-2">
                      <Label>Priority (higher wins for landing route)</Label>
                      <Input
                        type="number"
                        min={0}
                        max={1000}
                        value={draftRole.priority ?? 0}
                        onChange={(e) =>
                          setDraftRole((p) => ({ ...p, priority: Number(e.target.value) }))
                        }
                      />
                    </div>
                    <div className="space-y-2">
                      <Label>Landing route after login</Label>
                      <Input
                        value={draftRole.landing_route ?? ''}
                        onChange={(e) =>
                          setDraftRole((p) => ({ ...p, landing_route: e.target.value }))
                        }
                        placeholder="/admin"
                      />
                    </div>
                  </div>
                  <div className="flex justify-end mt-4">
                    <Button
                      onClick={() => updateRoleMut.mutate()}
                      disabled={!metaDirty || updateRoleMut.isPending}
                    >
                      <Save className="mr-2 h-4 w-4" />
                      {updateRoleMut.isPending ? 'Saving…' : 'Save details'}
                    </Button>
                  </div>
                </CardContent>
              </Card>

              {/* Page grid */}
              <Card>
                <CardHeader>
                  <div className="flex items-start justify-between gap-4">
                    <div>
                      <CardTitle className="text-base">Page access</CardTitle>
                      <p className="text-xs text-muted-foreground mt-1">
                        {draftPageIds.size} of {pages.length} pages enabled
                        {selectedRole.name === 'sys_admin' && (
                          <span className="ml-2">
                            (sys_admin always passes the access check regardless of these values)
                          </span>
                        )}
                      </p>
                    </div>
                    <Button
                      onClick={() => syncPagesMut.mutate()}
                      disabled={!pagesDirty || syncPagesMut.isPending}
                    >
                      <Save className="mr-2 h-4 w-4" />
                      {syncPagesMut.isPending ? 'Saving…' : 'Save permissions'}
                    </Button>
                  </div>
                </CardHeader>
                <CardContent>
                  <div className="space-y-6">
                    {pageGroups.map(([groupName, groupPages]) => {
                      const allChecked = groupPages.every((p) => draftPageIds.has(p.id));
                      const someChecked = groupPages.some((p) => draftPageIds.has(p.id));
                      return (
                        <div key={groupName}>
                          <div className="flex items-center justify-between mb-2 pb-2 border-b">
                            <h3 className="text-sm font-semibold">{groupName}</h3>
                            <button
                              type="button"
                              onClick={() => toggleGroup(groupPages)}
                              className="text-xs text-primary hover:underline"
                            >
                              {allChecked ? 'Clear all' : someChecked ? 'Select all' : 'Select all'}
                            </button>
                          </div>
                          <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2">
                            {groupPages.map((p) => (
                              <label
                                key={p.id}
                                className="flex items-start gap-2 p-2 rounded hover:bg-muted cursor-pointer"
                              >
                                <Checkbox
                                  checked={draftPageIds.has(p.id)}
                                  onCheckedChange={() => togglePage(p.id)}
                                  className="mt-0.5"
                                />
                                <div className="min-w-0">
                                  <div className="text-sm font-medium truncate">{p.label}</div>
                                  <div className="text-xs text-muted-foreground truncate font-mono">
                                    {p.key}
                                  </div>
                                </div>
                              </label>
                            ))}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                </CardContent>
              </Card>

              {/* Danger zone */}
              <Card className="border-destructive/30">
                <CardHeader>
                  <CardTitle className="text-base flex items-center gap-2 text-destructive">
                    <AlertTriangle className="h-4 w-4" />
                    Danger zone
                  </CardTitle>
                </CardHeader>
                <CardContent>
                  <div className="flex items-center justify-between gap-4">
                    <div className="text-sm text-muted-foreground">
                      {!selectedRole.deletable
                        ? 'This is a protected system role and cannot be deleted.'
                        : selectedRole.user_count > 0
                          ? `This role is assigned to ${selectedRole.user_count} user${selectedRole.user_count === 1 ? '' : 's'}. Reassign them before deleting.`
                          : 'No users currently have this role. Safe to delete.'}
                    </div>
                    <AlertDialog>
                      <AlertDialogTrigger asChild>
                        <Button
                          variant="destructive"
                          disabled={!selectedRole.deletable || selectedRole.user_count > 0 || deleteRoleMut.isPending}
                        >
                          <Trash2 className="mr-2 h-4 w-4" />
                          Delete role
                        </Button>
                      </AlertDialogTrigger>
                      <AlertDialogContent>
                        <AlertDialogHeader>
                          <AlertDialogTitle>Delete "{selectedRole.label}"?</AlertDialogTitle>
                          <AlertDialogDescription>
                            This permanently removes the role and all its page assignments.
                            This action cannot be undone.
                          </AlertDialogDescription>
                        </AlertDialogHeader>
                        <AlertDialogFooter>
                          <AlertDialogCancel>Cancel</AlertDialogCancel>
                          <AlertDialogAction
                            onClick={() => deleteRoleMut.mutate(selectedRole.id)}
                            className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
                          >
                            Delete
                          </AlertDialogAction>
                        </AlertDialogFooter>
                      </AlertDialogContent>
                    </AlertDialog>
                  </div>
                </CardContent>
              </Card>
            </div>
          ) : (
            <Card>
              <CardContent className="py-12 text-center text-muted-foreground">
                <Users className="h-8 w-8 mx-auto mb-2 opacity-40" />
                <p className="text-sm">Select a role from the list to edit it.</p>
              </CardContent>
            </Card>
          )}
        </div>
      </div>
    </>
  );
};

// --- Create role modal ---

const CreateRoleDialog = ({
  open, onOpenChange, onCreate, isPending,
}: {
  open: boolean;
  onOpenChange: (b: boolean) => void;
  onCreate: (payload: { name: string; label: string; priority: number; landing_route: string | null }) => void;
  isPending: boolean;
}) => {
  const [name, setName] = useState('');
  const [label, setLabel] = useState('');
  const [priority, setPriority] = useState('0');
  const [landingRoute, setLandingRoute] = useState('');

  useEffect(() => {
    if (!open) {
      setName(''); setLabel(''); setPriority('0'); setLandingRoute('');
    }
  }, [open]);

  const submit = () => {
    if (!name || !label) return;
    onCreate({
      name: name.trim(),
      label: label.trim(),
      priority: Number(priority) || 0,
      landing_route: landingRoute.trim() || null,
    });
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogTrigger asChild>
        <Button>
          <Plus className="mr-2 h-4 w-4" />
          New role
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Create new role</DialogTitle>
        </DialogHeader>
        <div className="space-y-4">
          <div className="space-y-2">
            <Label>Identifier *</Label>
            <Input
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="lowercase_with_underscores"
            />
            <p className="text-xs text-muted-foreground">
              Lowercase letters, digits and underscores. Must start with a letter.
            </p>
          </div>
          <div className="space-y-2">
            <Label>Display label *</Label>
            <Input
              value={label}
              onChange={(e) => setLabel(e.target.value)}
              placeholder="Customer Service Manager"
            />
          </div>
          <div className="grid grid-cols-2 gap-4">
            <div className="space-y-2">
              <Label>Priority</Label>
              <Input
                type="number"
                min={0}
                max={1000}
                value={priority}
                onChange={(e) => setPriority(e.target.value)}
              />
            </div>
            <div className="space-y-2">
              <Label>Landing route</Label>
              <Input
                value={landingRoute}
                onChange={(e) => setLandingRoute(e.target.value)}
                placeholder="/admin"
              />
            </div>
          </div>
        </div>
        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)}>Cancel</Button>
          <Button onClick={submit} disabled={!name || !label || isPending}>
            {isPending ? 'Creating…' : 'Create role'}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
};

RoleManagementPage.layout = (page: ReactNode) => <AdminLayout>{page}</AdminLayout>;

export default RoleManagementPage;
