"use client";

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/app/shadcn_components/ui/table";
import { Input } from "@/app/shadcn_components/ui/input";
import { Button } from "@/app/shadcn_components/ui/button";

import { useState } from "react";
import {
  Role,
  RoleDropdown,
  Collaborator,
} from "@/app/portfolio/[slug]/(portfolio)/settings/roles";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  canAdminister,
  type PortfolioPrivilege,
} from "@/app/lib/portfolioAdmin";
import {
  COLLABORATORS_QUERY_KEY,
  fetchCollaborators,
  type CollaboratorsResponse,
} from "./collaboratorsClient";
import { ConfirmDialog } from "@/app/components/ConfirmDialog";
import { useToast } from "@/app/hooks/use-toast";

type PendingInvitation = {
  invitationId: string;
  email: string;
  role: Role | "creator";
  createdAt: string;
};

async function getPortfolioInvitations(
  portfolioId: string,
): Promise<PendingInvitation[]> {
  const res = await fetch(`/api/portfolio/${portfolioId}/invitations`, {
    method: "GET",
    headers: { "Content-Type": "application/json" },
  });
  if (!res.ok) throw new Error("Failed to fetch invitations");
  const json = await res.json();
  const invitations = json?.invitations ?? [];
  return Array.isArray(invitations)
    ? invitations.map((i: any) => ({
        invitationId: String(i.invitationId),
        email: i.email ?? "",
        role: i.role,
        createdAt: i.createdAt,
      }))
    : [];
}

async function updatePortfolioUserRole(
  portfolioId: string,
  portfolioUserId: string,
  role: Role,
): Promise<void> {
  const res = await fetch(`/api/portfolio/${portfolioId}/collaborators`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ portfolioUserId, role }),
  });
  if (!res.ok) {
    const msg = await res.text().catch(() => "");
    throw new Error(msg || "Failed to update role");
  }
}

async function invitePortfolioUser(
  portfolioId: string,
  email: string,
  role: Role,
): Promise<void> {
  const res = await fetch(`/api/portfolio/${portfolioId}/collaborators`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, role }),
  });
  if (!res.ok) {
    const msg = await res.text().catch(() => "");
    throw new Error(msg || "Failed to invite user");
  }
}

async function removePortfolioUser(
  portfolioId: string,
  portfolioUserId: string,
): Promise<void> {
  const res = await fetch(`/api/portfolio/${portfolioId}/collaborators`, {
    method: "DELETE",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ portfolioUserId }),
  });
  if (!res.ok) {
    const msg = await res.text().catch(() => "");
    throw new Error(msg || "Failed to remove user");
  }
}

async function revokePortfolioInvitation(
  portfolioId: string,
  invitationId: string,
): Promise<void> {
  const res = await fetch(`/api/portfolio/${portfolioId}/invitations`, {
    method: "DELETE",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ invitationId }),
  });
  if (!res.ok) {
    const msg = await res.text().catch(() => "");
    throw new Error(msg || "Failed to revoke invitation");
  }
}

export function UsersPermissionsCard({ portfolioId }: { portfolioId: string }) {
  const [inviteEmail, setInviteEmail] = useState("");
  const [inviteRole, setInviteRole] = useState<Role>("read");
  const [pendingRemoval, setPendingRemoval] =
    useState<{ portfolioUserId: string; email: string } | null>(null);
  const [pendingRevoke, setPendingRevoke] =
    useState<{ invitationId: string; email: string } | null>(null);

  const { toast } = useToast();
  const queryClient = useQueryClient();

  const usersKey = COLLABORATORS_QUERY_KEY(portfolioId);
  const invitationsKey = ["portfolioInvitations", portfolioId];

  const {
    data: collaboratorsData,
    isLoading,
    isFetching,
    refetch,
  } = useQuery({
    queryKey: usersKey,
    queryFn: () => fetchCollaborators(portfolioId),
    enabled: !!portfolioId,
    refetchOnWindowFocus: false,
  });

  const collaborators = collaboratorsData?.users ?? [];
  const currentPrivilege: PortfolioPrivilege =
    collaboratorsData?.currentUser?.privilege ?? "none";
  const isAdmin = canAdminister(currentPrivilege);

  const {
    data: invitations = [],
    isLoading: invitationsLoading,
    isFetching: invitationsFetching,
  } = useQuery({
    queryKey: invitationsKey,
    queryFn: () => getPortfolioInvitations(portfolioId),
    // Only admins can see pending invitations — the GET endpoint also enforces
    // this; gating here avoids the unauthorised network request.
    enabled: !!portfolioId && isAdmin,
    refetchOnWindowFocus: false,
  });

  const invalidateBoth = () => {
    queryClient.invalidateQueries({ queryKey: usersKey });
    queryClient.invalidateQueries({ queryKey: invitationsKey });
  };

  const changeRoleMutation = useMutation({
    mutationFn: ({
      portfolioUserId,
      role,
    }: {
      portfolioUserId: string;
      role: Role;
    }) => updatePortfolioUserRole(portfolioId, portfolioUserId, role),

    onMutate: async ({ portfolioUserId, role }) => {
      await queryClient.cancelQueries({ queryKey: usersKey });
      const previous =
        queryClient.getQueryData<CollaboratorsResponse>(usersKey);
      queryClient.setQueryData<CollaboratorsResponse>(usersKey, (old) =>
        old
          ? {
              ...old,
              users: old.users.map((c) =>
                c.portfolioUserId === portfolioUserId ? { ...c, role } : c,
              ),
            }
          : old,
      );
      return { previous };
    },
    onError: (err, _vars, context) => {
      if (context?.previous) {
        queryClient.setQueryData(usersKey, context.previous);
      }
      console.error("Failed to update role:", err);
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: usersKey });
    },
  });

  const inviteUserMutation = useMutation({
    mutationFn: ({
      email,
      role,
    }: {
      email: string;
      role: Role;
    }) => invitePortfolioUser(portfolioId, email, role),
    onSuccess: (_data, vars) => {
      invalidateBoth();
      setInviteEmail("");
      toast({
        title: "Invitation sent",
        description: `We've emailed ${vars.email} an invitation to this portfolio.`,
      });
    },
    onError: (err) => {
      console.error("Invite failed:", err);
      toast({
        title: "Couldn't send invitation",
        description: err instanceof Error ? err.message : "Please try again.",
        variant: "destructive",
      });
    },
  });

  const removeUserMutation = useMutation({
    mutationFn: (portfolioUserId: string) =>
      removePortfolioUser(portfolioId, portfolioUserId),

    onMutate: async (portfolioUserId) => {
      await queryClient.cancelQueries({ queryKey: usersKey });
      const previous =
        queryClient.getQueryData<CollaboratorsResponse>(usersKey);
      queryClient.setQueryData<CollaboratorsResponse>(usersKey, (old) =>
        old
          ? {
              ...old,
              users: old.users.filter(
                (c) => c.portfolioUserId !== portfolioUserId,
              ),
            }
          : old,
      );
      return { previous };
    },
    onSuccess: (_data, _portfolioUserId) => {
      const email = pendingRemoval?.email;
      toast({
        title: "User removed",
        description: email
          ? `${email} no longer has access to this portfolio.`
          : "User no longer has access to this portfolio.",
      });
    },
    onError: (err, _vars, context) => {
      if (context?.previous) {
        queryClient.setQueryData(usersKey, context.previous);
      }
      console.error("Failed to remove user:", err);
      toast({
        title: "Couldn't remove user",
        description: err instanceof Error ? err.message : "Please try again.",
        variant: "destructive",
      });
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: usersKey });
      setPendingRemoval(null);
    },
  });

  const revokeInvitationMutation = useMutation({
    mutationFn: (invitationId: string) =>
      revokePortfolioInvitation(portfolioId, invitationId),

    onMutate: async (invitationId) => {
      await queryClient.cancelQueries({ queryKey: invitationsKey });
      const previous =
        queryClient.getQueryData<PendingInvitation[]>(invitationsKey);
      queryClient.setQueryData<PendingInvitation[]>(invitationsKey, (old) =>
        (old ?? []).filter((i) => i.invitationId !== invitationId),
      );
      return { previous };
    },
    onSuccess: () => {
      const email = pendingRevoke?.email;
      toast({
        title: "Invitation revoked",
        description: email
          ? `${email}'s invitation has been cancelled.`
          : "The invitation has been cancelled.",
      });
    },
    onError: (err, _vars, context) => {
      if (context?.previous) {
        queryClient.setQueryData(invitationsKey, context.previous);
      }
      console.error("Failed to revoke invitation:", err);
      toast({
        title: "Couldn't revoke invitation",
        description: err instanceof Error ? err.message : "Please try again.",
        variant: "destructive",
      });
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: invitationsKey });
      setPendingRevoke(null);
    },
  });

  function handleInvite() {
    inviteUserMutation.mutate({
      email: inviteEmail,
      role: inviteRole,
    });
  }

  function onChangeRole(portfolioUserId: string, role: Role) {
    changeRoleMutation.mutate({ portfolioUserId, role });
  }

  function onRemove(portfolioUserId: string, email: string) {
    setPendingRemoval({ portfolioUserId, email });
  }

  function onRevokeInvitation(invitationId: string, email: string) {
    setPendingRevoke({ invitationId, email });
  }

  function confirmRemove() {
    if (!pendingRemoval) return;
    removeUserMutation.mutate(pendingRemoval.portfolioUserId);
  }

  function confirmRevoke() {
    if (!pendingRevoke) return;
    revokeInvitationMutation.mutate(pendingRevoke.invitationId);
  }

  return (
    <div className="rounded-md border border-gray-700 mt-2">
      <Table>
        <TableBody>
          <TableRow>
            <TableHead className="text-brandblue">
              Users Permission:
              <p className="text-xs text-gray-500">Add users and manage roles</p>
            </TableHead>
            <TableCell className="text-right">
              <Button
                variant="outline"
                onClick={() => refetch()}
                disabled={isFetching || isLoading}
              >
                {isFetching || isLoading ? "Loading..." : "Refresh Users"}
              </Button>
            </TableCell>
          </TableRow>

          {/* Invite row — admin-only */}
          {isAdmin && (
            <TableRow>
              <TableHead className="text-brandblue">
                Add a user
                <p className="text-xs text-gray-500">
                  Invite by email and choose a role
                </p>
              </TableHead>
              <TableCell className="flex gap-2 items-center">
                <Input
                  type="email"
                  placeholder="email@example.com"
                  value={inviteEmail}
                  onChange={(e) => setInviteEmail(e.target.value)}
                />
                <div className="min-w-40">
                  <RoleDropdown
                    value={inviteRole}
                    onChange={setInviteRole}
                    allowAdminPromotion
                  />
                </div>
              </TableCell>
              <TableCell className="text-right">
                <Button
                  className="w-28"
                  onClick={handleInvite}
                  disabled={!inviteEmail || inviteUserMutation.isPending}
                >
                  {inviteUserMutation.isPending ? "Inviting..." : "Invite"}
                </Button>
              </TableCell>
            </TableRow>
          )}

          {/* Current collaborators list */}
          <TableRow>
            <TableHead className="text-brandblue">
              Current users
              <p className="text-xs text-gray-500">
                Update roles or remove access
              </p>
            </TableHead>
            <TableCell colSpan={2}>
              <div className="rounded-md border border-gray-200">
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead>Name</TableHead>
                      <TableHead>Email</TableHead>
                      <TableHead>Role</TableHead>
                      <TableHead className="text-right">Actions</TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {isLoading ? (
                      <TableRow>
                        <TableCell colSpan={4} className="text-sm text-gray-500">
                          Loading…
                        </TableCell>
                      </TableRow>
                    ) : collaborators.length === 0 ? (
                      <TableRow>
                        <TableCell colSpan={4} className="text-sm text-gray-500">
                          No users yet.
                        </TableCell>
                      </TableRow>
                    ) : (
                      collaborators.map((c) => (
                        <TableRow key={c.email}>
                          <TableCell>{c.name || "—"}</TableCell>
                          <TableCell>{c.email}</TableCell>
                          <TableCell className="min-w-40">
                            {c.role === "creator" || !isAdmin ? (
                              <span className="text-xs font-medium text-gray-500 px-2 py-1 bg-gray-100 rounded-md capitalize">
                                {c.role}
                              </span>
                            ) : (
                              <RoleDropdown
                                value={c.role as Role}
                                onChange={(r) =>
                                  onChangeRole(c.portfolioUserId, r)
                                }
                                allowAdminPromotion
                              />
                            )}
                          </TableCell>
                          <TableCell className="text-right">
                            {c.role !== "creator" && isAdmin && (
                              <Button
                                variant="destructive"
                                className="bg-red-700"
                                onClick={() =>
                                  onRemove(c.portfolioUserId, c.email)
                                }
                                disabled={removeUserMutation.isPending}
                              >
                                {removeUserMutation.isPending &&
                                removeUserMutation.variables ===
                                  c.portfolioUserId
                                  ? "Removing..."
                                  : "Remove"}
                              </Button>
                            )}
                          </TableCell>
                        </TableRow>
                      ))
                    )}
                  </TableBody>
                </Table>
              </div>
            </TableCell>
          </TableRow>

          {/* Pending invitations list — admin-only */}
          {isAdmin && (
          <TableRow>
            <TableHead className="text-brandblue">
              Pending invitations
              <p className="text-xs text-gray-500">
                Emails invited but not yet signed in
              </p>
            </TableHead>
            <TableCell colSpan={2}>
              <div className="rounded-md border border-gray-200">
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead>Email</TableHead>
                      <TableHead>Role</TableHead>
                      <TableHead>Invited</TableHead>
                      <TableHead className="text-right">Actions</TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {invitationsLoading || invitationsFetching ? (
                      <TableRow>
                        <TableCell colSpan={4} className="text-sm text-gray-500">
                          Loading…
                        </TableCell>
                      </TableRow>
                    ) : invitations.length === 0 ? (
                      <TableRow>
                        <TableCell colSpan={4} className="text-sm text-gray-500">
                          No pending invitations.
                        </TableCell>
                      </TableRow>
                    ) : (
                      invitations.map((i) => (
                        <TableRow key={i.invitationId}>
                          <TableCell>{i.email}</TableCell>
                          <TableCell className="capitalize">{i.role}</TableCell>
                          <TableCell className="text-sm text-gray-500">
                            {new Date(i.createdAt).toLocaleDateString()}
                          </TableCell>
                          <TableCell className="text-right">
                            <Button
                              variant="destructive"
                              className="bg-red-700"
                              onClick={() =>
                                onRevokeInvitation(i.invitationId, i.email)
                              }
                              disabled={revokeInvitationMutation.isPending}
                            >
                              {revokeInvitationMutation.isPending &&
                              revokeInvitationMutation.variables ===
                                i.invitationId
                                ? "Revoking..."
                                : "Revoke"}
                            </Button>
                          </TableCell>
                        </TableRow>
                      ))
                    )}
                  </TableBody>
                </Table>
              </div>
            </TableCell>
          </TableRow>
          )}
        </TableBody>
      </Table>

      <ConfirmDialog
        open={pendingRemoval !== null}
        onOpenChange={(open) => !open && setPendingRemoval(null)}
        title="Remove user from this portfolio?"
        description={
          pendingRemoval ? (
            <>
              <span className="font-medium">{pendingRemoval.email}</span> will
              immediately lose access. They can be re-invited later.
            </>
          ) : null
        }
        confirmLabel="Remove"
        destructive
        isPending={removeUserMutation.isPending}
        onConfirm={confirmRemove}
      />

      <ConfirmDialog
        open={pendingRevoke !== null}
        onOpenChange={(open) => !open && setPendingRevoke(null)}
        title="Revoke this pending invitation?"
        description={
          pendingRevoke ? (
            <>
              <span className="font-medium">{pendingRevoke.email}</span> won&apos;t
              be able to accept this invitation. You can invite them again
              later.
            </>
          ) : null
        }
        confirmLabel="Revoke"
        destructive
        isPending={revokeInvitationMutation.isPending}
        onConfirm={confirmRevoke}
      />
    </div>
  );
}
