"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { Plus } from "lucide-react";

import ConfirmDialog from "@/components/common/ConfirmDialog";
import SearchInput from "@/components/common/SearchInput";
import { useToast } from "@/components/common/ToastProvider";
import { Button } from "@/components/ui/button";
import UserFormDialog from "@/components/users/UserFormDialog";
import UserTable from "@/components/users/UserTable";
import { api } from "@/lib/api";
import { hasAllPermissions, hasPermission } from "@/lib/permissions";
import type { CurrentUser, Role } from "@/types/rbac";
import type { User, UserPayload, UserRole } from "@/types/user";

const pageSize = 10;

type StatusFilter = "all" | "active" | "inactive";

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 getSortableValue(user: User, key: string) {
  const value = user[key as keyof User];
  return typeof value === "string" ? value.toLowerCase() : value;
}

export default function UsersPage() {
  const { showToast } = useToast();
  const [users, setUsers] = useState<User[]>([]);
  const [roles, setRoles] = useState<Role[]>([]);
  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 [roleFilter, setRoleFilter] = useState<UserRole | "all">("all");
  const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
  const [sortKey, setSortKey] = useState("created_at");
  const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc");
  const [page, setPage] = useState(1);
  const [formOpen, setFormOpen] = useState(false);
  const [selectedUser, setSelectedUser] = useState<User | null>(null);
  const [deleteUser, setDeleteUser] = useState<User | null>(null);

  const loadUsers = useCallback(async () => {
    try {
      const [usersResponse, meResponse] = await Promise.all([
        api.get<User[]>("/users"),
        api.get<CurrentUser>("/me"),
      ]);
      setUsers(usersResponse.data);
      setCurrentUser(meResponse.data);

      if (hasPermission(meResponse.data, "roles.read")) {
        const rolesResponse = await api.get<Role[]>("/roles");
        setRoles(rolesResponse.data);
      } else {
        const userRoles = usersResponse.data.map((user) => user.primary_role);
        setRoles(userRoles.filter((role, index, all) => (
          all.findIndex((item) => item.id === role.id) === index
        )));
      }
    } catch (err) {
      setError(getErrorMessage(err));
    } finally {
      setLoading(false);
    }
  }, []);

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

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

    return users
      .filter((user) => {
        if (!term) {
          return true;
        }

        return [
          user.first_name,
          user.last_name,
          user.username,
          user.email,
          user.primary_role.display_name,
          user.primary_role.name,
        ].some((value) => value.toLowerCase().includes(term));
      })
      .filter((user) => roleFilter === "all" || user.primary_role.name === roleFilter)
      .filter((user) => {
        if (statusFilter === "all") {
          return true;
        }

        return statusFilter === "active" ? user.is_active : !user.is_active;
      })
      .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;
      });
  }, [roleFilter, search, sortDirection, sortKey, statusFilter, users]);

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

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

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

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

  function openEditDialog(user: User) {
    setSelectedUser(user);
    setFormError("");
    setFormOpen(true);
  }

  async function saveUser(payload: UserPayload) {
    setPending(true);
    setFormError("");

    try {
      if (selectedUser) {
        const response = await api.put<User>(`/users/${selectedUser.id}`, payload);
        setUsers((current) => current.map((user) => (
          user.id === selectedUser.id ? response.data : user
        )));
        showToast({ type: "success", title: "Benutzer aktualisiert", description: response.data.username });
      } else {
        const response = await api.post<User>("/users", payload);
        setUsers((current) => [response.data, ...current]);
        showToast({ type: "success", title: "Benutzer erstellt", description: response.data.username });
      }

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

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

    setPending(true);
    setDeleteError("");

    try {
      await api.delete(`/users/${deleteUser.id}`);
      setUsers((current) => current.filter((user) => user.id !== deleteUser.id));
      showToast({ type: "success", title: "Benutzer gelöscht", description: deleteUser.username });
      setDeleteUser(null);
    } catch (err) {
      const message = getErrorMessage(err);
      setDeleteError(message);
      showToast({ type: "error", title: "Benutzer 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">Benutzer</h1>
          <p className="mt-1 text-sm text-slate-500">
            {filteredUsers.length} von {users.length} Benutzern
          </p>
        </div>

        {hasAllPermissions(currentUser, ["users.create", "roles.assign"]) && (
          <Button type="button" onClick={openCreateDialog}>
            <Plus size={16} />
            Neuer Benutzer
          </Button>
        )}
      </div>

      <div className="flex flex-col gap-3 rounded-lg border bg-white p-4 lg:flex-row lg:items-center">
        <SearchInput
          value={search}
          onChange={(value) => {
            setSearch(value);
            setPage(1);
          }}
          placeholder="Benutzer suchen"
        />

        <select
          value={roleFilter}
          onChange={(event) => {
            setRoleFilter(event.target.value as UserRole | "all");
            setPage(1);
          }}
          className="h-8 rounded-lg border border-input bg-transparent px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
        >
          <option value="all">Alle Rollen</option>
          {roles.map((role) => (
            <option key={role.id} value={role.name}>
              {role.display_name}
            </option>
          ))}
        </select>

        <select
          value={statusFilter}
          onChange={(event) => {
            setStatusFilter(event.target.value as StatusFilter);
            setPage(1);
          }}
          className="h-8 rounded-lg border border-input bg-transparent px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
        >
          <option value="all">Alle Status</option>
          <option value="active">Aktiv</option>
          <option value="inactive">Inaktiv</option>
        </select>
      </div>

      <UserTable
        users={pageUsers}
        loading={loading}
        error={error}
        sortKey={sortKey}
        sortDirection={sortDirection}
        canUpdate={hasPermission(currentUser, "users.update")}
        canDelete={hasPermission(currentUser, "users.delete")}
        onSort={handleSort}
        onEdit={openEditDialog}
        onDelete={setDeleteUser}
      />

      <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>

      <UserFormDialog
        open={formOpen}
        user={selectedUser}
        pending={pending}
        serverError={formError}
        roles={roles}
        onOpenChange={setFormOpen}
        onSubmit={saveUser}
      />

      <ConfirmDialog
        open={Boolean(deleteUser)}
        title="Benutzer löschen"
        description="Möchten Sie den Benutzer wirklich löschen?"
        pending={pending}
        onOpenChange={(open) => {
          if (!open) {
            setDeleteUser(null);
            setDeleteError("");
          }
        }}
        onConfirm={confirmDelete}
      >
        {deleteUser && (
          <dl className="grid gap-2 text-sm">
            <div className="flex justify-between gap-4">
              <dt className="text-slate-500">Benutzername</dt>
              <dd className="font-medium">{deleteUser.username}</dd>
            </div>
            <div className="flex justify-between gap-4">
              <dt className="text-slate-500">E-Mail</dt>
              <dd className="font-medium">{deleteUser.email}</dd>
            </div>
            <div className="flex justify-between gap-4">
              <dt className="text-slate-500">Rolle</dt>
              <dd className="font-medium">{deleteUser.primary_role.display_name}</dd>
            </div>
            {deleteError && <p className="text-red-600">{deleteError}</p>}
          </dl>
        )}
      </ConfirmDialog>
    </div>
  );
}
