"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import type { FormEvent } from "react";
import { Edit, Eye, Shield, Trash2 } from "lucide-react";

import ConfirmDialog from "@/components/common/ConfirmDialog";
import DataTable, { type DataTableColumn } from "@/components/common/DataTable";
import SearchInput from "@/components/common/SearchInput";
import { useToast } from "@/components/common/ToastProvider";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { api } from "@/lib/api";
import { hasPermission } from "@/lib/permissions";
import type { CurrentUser, Permission, Role, RolePayload } from "@/types/rbac";

const pageSize = 10;

function getErrorMessage(error: unknown) {
  if (typeof error === "object" && error !== null && "response" in error) {
    const response = (error as { response?: { data?: { detail?: string } } }).response;
    return response?.data?.detail ?? "Aktion konnte nicht abgeschlossen werden";
  }

  return "Aktion konnte nicht abgeschlossen werden";
}

function formatDate(value: string) {
  return new Intl.DateTimeFormat("de-DE", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
  }).format(new Date(value));
}

function getSortableValue(role: Role, key: string) {
  const value = role[key as keyof Role];
  return typeof value === "string" ? value.toLowerCase() : value;
}

export default function RolesPage() {
  const { showToast } = useToast();
  const [roles, setRoles] = useState<Role[]>([]);
  const [permissions, setPermissions] = useState<Permission[]>([]);
  const [currentUser, setCurrentUser] = useState<CurrentUser | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [formError, setFormError] = useState("");
  const [deleteError, setDeleteError] = useState("");
  const [pending, setPending] = useState(false);
  const [search, setSearch] = useState("");
  const [sortKey, setSortKey] = useState("name");
  const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
  const [page, setPage] = useState(1);
  const [selectedRole, setSelectedRole] = useState<Role | null>(null);
  const [detailsRole, setDetailsRole] = useState<Role | null>(null);
  const [deleteRole, setDeleteRole] = useState<Role | null>(null);
  const [formOpen, setFormOpen] = useState(false);

  const loadRoles = useCallback(async () => {
    try {
      const [rolesResponse, permissionsResponse, meResponse] = await Promise.all([
        api.get<Role[]>("/roles"),
        api.get<Permission[]>("/permissions"),
        api.get<CurrentUser>("/me"),
      ]);
      setRoles(rolesResponse.data);
      setPermissions(permissionsResponse.data);
      setCurrentUser(meResponse.data);
    } catch (err) {
      setError(getErrorMessage(err));
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    queueMicrotask(() => {
      void loadRoles();
    });
  }, [loadRoles]);

  const filteredRoles = useMemo(() => {
    const term = search.trim().toLowerCase();

    return roles
      .filter((role) => {
        if (!term) {
          return true;
        }

        return [
          role.name,
          role.display_name,
          role.description,
        ].some((value) => value.toLowerCase().includes(term));
      })
      .sort((first, second) => {
        const firstValue = getSortableValue(first, sortKey);
        const secondValue = getSortableValue(second, sortKey);

        if (firstValue === secondValue) {
          return 0;
        }

        const result = firstValue > secondValue ? 1 : -1;
        return sortDirection === "asc" ? result : -result;
      });
  }, [roles, search, sortDirection, sortKey]);

  const pageCount = Math.max(1, Math.ceil(filteredRoles.length / pageSize));
  const currentPage = Math.min(page, pageCount);
  const pageRoles = filteredRoles.slice(
    (currentPage - 1) * pageSize,
    currentPage * pageSize,
  );

  const columns: DataTableColumn<Role>[] = [
    {
      key: "display_name",
      label: "Rolle",
      sortable: true,
      render: (role) => (
        <div>
          <p className="font-medium text-slate-950">{role.display_name}</p>
          <p className="text-xs text-slate-500">{role.name}</p>
        </div>
      ),
    },
    {
      key: "description",
      label: "Beschreibung",
      sortable: true,
      render: (role) => role.description || "-",
    },
    {
      key: "is_system",
      label: "Typ",
      sortable: true,
      render: (role) => (
        <span className="inline-flex h-6 items-center rounded-full bg-slate-100 px-2 text-xs font-medium text-slate-700">
          {role.is_system ? "Systemrolle" : "Individuell"}
        </span>
      ),
    },
    {
      key: "permissions",
      label: "Berechtigungen",
      render: (role) => `${role.permissions.length}`,
    },
    {
      key: "created_at",
      label: "Erstellt",
      sortable: true,
      render: (role) => formatDate(role.created_at),
    },
    {
      key: "actions",
      label: "Aktionen",
      className: "px-4 py-3 text-right",
      render: (role) => (
        <div className="flex justify-end gap-2">
          <Button type="button" variant="ghost" size="icon-sm" onClick={() => setDetailsRole(role)} title="Details">
            <Eye size={16} />
          </Button>
          {hasPermission(currentUser, "roles.update") && (
            <Button type="button" variant="ghost" size="icon-sm" onClick={() => openEditDialog(role)} title="Bearbeiten">
              <Edit size={16} />
            </Button>
          )}
          {hasPermission(currentUser, "roles.delete") && !role.is_system && (
            <Button type="button" variant="destructive" size="icon-sm" onClick={() => setDeleteRole(role)} title="Löschen">
              <Trash2 size={16} />
            </Button>
          )}
        </div>
      ),
    },
  ];

  function handleSort(key: string) {
    if (sortKey === key) {
      setSortDirection((current) => (current === "asc" ? "desc" : "asc"));
      return;
    }

    setSortKey(key);
    setSortDirection("asc");
  }

  function openCreateDialog() {
    setSelectedRole(null);
    setFormError("");
    setFormOpen(true);
  }

  function openEditDialog(role: Role) {
    setSelectedRole(role);
    setFormError("");
    setFormOpen(true);
  }

  async function saveRole(payload: RolePayload) {
    setPending(true);
    setFormError("");

    try {
      if (selectedRole) {
        const response = await api.put<Role>(`/roles/${selectedRole.id}`, payload);
        setRoles((current) => current.map((role) => (
          role.id === selectedRole.id ? response.data : role
        )));
        showToast({ type: "success", title: "Rolle aktualisiert", description: response.data.display_name });
      } else {
        const response = await api.post<Role>("/roles", payload);
        setRoles((current) => [response.data, ...current]);
        showToast({ type: "success", title: "Rolle erstellt", description: response.data.display_name });
      }

      setFormOpen(false);
    } catch (err) {
      const message = getErrorMessage(err);
      setFormError(message);
      showToast({ type: "error", title: "Rolle konnte nicht gespeichert werden", description: message });
    } finally {
      setPending(false);
    }
  }

  async function confirmDelete() {
    if (!deleteRole) {
      return;
    }

    setPending(true);
    setDeleteError("");

    try {
      await api.delete(`/roles/${deleteRole.id}`);
      setRoles((current) => current.filter((role) => role.id !== deleteRole.id));
      showToast({ type: "success", title: "Rolle gelöscht", description: deleteRole.display_name });
      setDeleteRole(null);
    } catch (err) {
      const message = getErrorMessage(err);
      setDeleteError(message);
      showToast({ type: "error", title: "Rolle konnte nicht gelöscht werden", description: message });
    } finally {
      setPending(false);
    }
  }

  return (
    <div className="space-y-6">
      <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
        <div>
          <h1 className="text-3xl font-bold text-slate-950">Rollen & Rechte</h1>
          <p className="mt-1 text-sm text-slate-500">
            {filteredRoles.length} von {roles.length} Rollen
          </p>
        </div>

        {hasPermission(currentUser, "roles.create") && (
          <Button type="button" onClick={openCreateDialog}>
            <Shield size={16} />
            Neue Rolle
          </Button>
        )}
      </div>

      <div className="rounded-lg border bg-white p-4">
        <SearchInput
          value={search}
          onChange={(value) => {
            setSearch(value);
            setPage(1);
          }}
          placeholder="Rollen suchen"
        />
      </div>

      <DataTable
        columns={columns}
        rows={pageRoles}
        rowKey={(role) => role.id}
        sortKey={sortKey}
        sortDirection={sortDirection}
        loading={loading}
        error={error}
        emptyTitle="Keine Rollen gefunden"
        emptyDescription="Passe Suche oder Filter an oder erstelle eine neue Rolle."
        onSort={handleSort}
      />

      <div className="flex items-center justify-between text-sm text-slate-600">
        <span>
          Seite {currentPage} von {pageCount}
        </span>
        <div className="flex gap-2">
          <Button
            type="button"
            variant="outline"
            disabled={currentPage === 1}
            onClick={() => setPage((current) => Math.max(1, current - 1))}
          >
            Zurück
          </Button>
          <Button
            type="button"
            variant="outline"
            disabled={currentPage === pageCount}
            onClick={() => setPage((current) => Math.min(pageCount, current + 1))}
          >
            Weiter
          </Button>
        </div>
      </div>

      <RoleFormDialog
        key={selectedRole?.id ?? "new"}
        open={formOpen}
        role={selectedRole}
        permissions={permissions}
        pending={pending}
        serverError={formError}
        onOpenChange={setFormOpen}
        onSubmit={saveRole}
      />

      <RoleDetailDialog
        role={detailsRole}
        onOpenChange={(open) => {
          if (!open) {
            setDetailsRole(null);
          }
        }}
      />

      <ConfirmDialog
        open={Boolean(deleteRole)}
        title="Rolle löschen"
        description="Möchten Sie diese Rolle wirklich löschen?"
        pending={pending}
        onOpenChange={(open) => {
          if (!open) {
            setDeleteRole(null);
            setDeleteError("");
          }
        }}
        onConfirm={confirmDelete}
      >
        {deleteRole && (
          <dl className="grid gap-2 text-sm">
            <div className="flex justify-between gap-4">
              <dt className="text-slate-500">Name</dt>
              <dd className="font-medium">{deleteRole.display_name}</dd>
            </div>
            <div className="flex justify-between gap-4">
              <dt className="text-slate-500">Systemrolle</dt>
              <dd className="font-medium">{deleteRole.is_system ? "Ja" : "Nein"}</dd>
            </div>
            {deleteError && <p className="text-red-600">{deleteError}</p>}
          </dl>
        )}
      </ConfirmDialog>
    </div>
  );
}

function RoleFormDialog({
  open,
  role,
  permissions,
  pending,
  serverError,
  onOpenChange,
  onSubmit,
}: {
  open: boolean;
  role: Role | null;
  permissions: Permission[];
  pending: boolean;
  serverError?: string;
  onOpenChange: (open: boolean) => void;
  onSubmit: (payload: RolePayload) => Promise<void>;
}) {
  const initialPermissionIds = role?.permissions.map((permission) => permission.id) ?? [];
  const [name, setName] = useState(role?.name ?? "");
  const [displayName, setDisplayName] = useState(role?.display_name ?? "");
  const [description, setDescription] = useState(role?.description ?? "");
  const [permissionIds, setPermissionIds] = useState<number[]>(initialPermissionIds);

  if (!open) {
    return null;
  }

  const groupedPermissions = permissions.reduce<Record<string, Permission[]>>((groups, permission) => {
    groups[permission.module] = groups[permission.module] ?? [];
    groups[permission.module].push(permission);
    return groups;
  }, {});

  const valid = name.trim().length >= 3 && displayName.trim().length >= 2;

  function togglePermission(permissionId: number) {
    setPermissionIds((current) => (
      current.includes(permissionId)
        ? current.filter((id) => id !== permissionId)
        : [...current, permissionId]
    ));
  }

  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    if (!valid) {
      return;
    }

    await onSubmit({
      name,
      display_name: displayName,
      description,
      permission_ids: permissionIds,
    });
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-3xl">
        <form onSubmit={handleSubmit} className="space-y-5">
          <DialogHeader>
            <DialogTitle>{role ? "Rolle bearbeiten" : "Rolle erstellen"}</DialogTitle>
            <DialogDescription>
              Rollenstammdaten und Berechtigungen verwalten.
            </DialogDescription>
          </DialogHeader>

          <div className="grid gap-4 sm:grid-cols-2">
            <div className="space-y-1.5">
              <Label>Technischer Name</Label>
              <Input value={name} onChange={(event) => setName(event.target.value)} disabled={role?.is_system} />
            </div>
            <div className="space-y-1.5">
              <Label>Anzeigename</Label>
              <Input value={displayName} onChange={(event) => setDisplayName(event.target.value)} />
            </div>
            <div className="space-y-1.5 sm:col-span-2">
              <Label>Beschreibung</Label>
              <Input value={description} onChange={(event) => setDescription(event.target.value)} />
            </div>
          </div>

          <div className="max-h-80 overflow-auto rounded-lg border">
            {Object.entries(groupedPermissions).map(([module, modulePermissions]) => (
              <div key={module} className="border-b p-4 last:border-b-0">
                <h3 className="mb-3 text-sm font-semibold capitalize text-slate-900">{module}</h3>
                <div className="grid gap-2 sm:grid-cols-2">
                  {modulePermissions.map((permission) => (
                    <label key={permission.id} className="flex items-start gap-2 rounded border p-2 text-sm">
                      <input
                        type="checkbox"
                        checked={permissionIds.includes(permission.id)}
                        onChange={() => togglePermission(permission.id)}
                      />
                      <span>
                        <span className="block font-medium">{permission.display_name}</span>
                        <span className="block text-xs text-slate-500">{permission.name}</span>
                      </span>
                    </label>
                  ))}
                </div>
              </div>
            ))}
          </div>

          {serverError && <p className="text-sm text-red-600">{serverError}</p>}

          <DialogFooter>
            <Button type="button" variant="outline" disabled={pending} onClick={() => onOpenChange(false)}>
              Abbrechen
            </Button>
            <Button type="submit" disabled={!valid || pending}>
              {pending ? "Speichern..." : "Speichern"}
            </Button>
          </DialogFooter>
        </form>
      </DialogContent>
    </Dialog>
  );
}

function RoleDetailDialog({
  role,
  onOpenChange,
}: {
  role: Role | null;
  onOpenChange: (open: boolean) => void;
}) {
  return (
    <Dialog open={Boolean(role)} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-2xl">
        {role && (
          <>
            <DialogHeader>
              <DialogTitle>{role.display_name}</DialogTitle>
              <DialogDescription>{role.description || role.name}</DialogDescription>
            </DialogHeader>
            <div className="space-y-4">
              <div className="grid gap-3 text-sm sm:grid-cols-2">
                <div>
                  <p className="text-slate-500">Technischer Name</p>
                  <p className="font-medium">{role.name}</p>
                </div>
                <div>
                  <p className="text-slate-500">Typ</p>
                  <p className="font-medium">{role.is_system ? "Systemrolle" : "Individuell"}</p>
                </div>
              </div>
              <div>
                <p className="mb-2 text-sm font-medium">Berechtigungen</p>
                <div className="flex flex-wrap gap-2">
                  {role.permissions.map((permission) => (
                    <span key={permission.id} className="rounded-full bg-slate-100 px-2 py-1 text-xs text-slate-700">
                      {permission.name}
                    </span>
                  ))}
                </div>
              </div>
            </div>
          </>
        )}
      </DialogContent>
    </Dialog>
  );
}
