/**
 * Copyright (c) 2023-present Plane Software, Inc. and contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 * See the LICENSE file for details.
 */

import { Fragment, useState } from "react";
import type { ReactNode } from "react";
import { Dialog, Transition } from "@headlessui/react";
import useSWR from "swr";
import { Ban, CheckCircle2, Loader, ShieldCheck, Trash2, UsersRound, X } from "lucide-react";
// plane imports
import { Button } from "@plane/propel/button";
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
import { InstanceWorkspaceService } from "@plane/services";
import type { TInstanceWorkspaceFeature, TInstanceWorkspaceMember } from "@plane/types";
import { CustomSelect, ToggleSwitch } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks
import { useWorkspace } from "@/hooks/store";

type TWorkspaceAdminModalProps = {
  isOpen: boolean;
  onClose: () => void;
  workspaceId: string | null;
};

const instanceWorkspaceService = new InstanceWorkspaceService();

const ROLE_OPTIONS = [
  { label: "Гость", value: 5 },
  { label: "Участник", value: 15 },
  { label: "Администратор", value: 20 },
];

const ROLE_LABELS: Record<number, string> = {
  5: "Гость",
  15: "Участник",
  20: "Администратор",
};

const BAN_DURATION_OPTIONS = [
  { label: "На 24 часа", value: "1d", hours: 24 },
  { label: "На 7 дней", value: "7d", hours: 24 * 7 },
  { label: "На 30 дней", value: "30d", hours: 24 * 30 },
  { label: "До ручного разбана", value: "manual", hours: null },
];

const ACCESS_MODE_LABELS: Record<TInstanceWorkspaceFeature["access_mode"], string> = {
  all_workspace_members: "Весь воркспейс",
  admins_only: "Только админы",
  selected_projects: "По контурам",
  selected_members: "По людям",
};

function getMemberName(member: TInstanceWorkspaceMember) {
  return member.member.display_name || member.member.email || "Пользователь";
}

function getErrorMessage(error: unknown, fallback: string) {
  if (error && typeof error === "object" && "error" in error && typeof error.error === "string") return error.error;
  return fallback;
}

function getBanUntilIso(duration: string) {
  const option = BAN_DURATION_OPTIONS.find((item) => item.value === duration);
  if (!option?.hours) return null;

  return new Date(Date.now() + option.hours * 60 * 60 * 1000).toISOString();
}

function formatBanUntil(bannedUntil?: string | null) {
  if (!bannedUntil) return "до ручного разбана";

  return `до ${new Intl.DateTimeFormat("ru-RU", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  }).format(new Date(bannedUntil))}`;
}

function RoleSelect(props: { disabled: boolean; onChange: (role: number) => void; value: number }) {
  const { disabled, onChange, value } = props;

  return (
    <CustomSelect
      value={value}
      label={<span className="truncate">{ROLE_LABELS[value] ?? "Роль"}</span>}
      onChange={(role: number) => onChange(Number(role))}
      buttonClassName="nodedc-settings-select h-10 min-h-10 w-full justify-between px-3 text-12"
      className="w-full"
      disabled={disabled}
      input
      maxHeight="sm"
      optionsClassName="z-[80] min-w-[12rem]"
      placement="bottom-start"
    >
      {ROLE_OPTIONS.map((role) => (
        <CustomSelect.Option key={role.value} value={role.value} className="w-full">
          <span className="text-12 font-medium">{role.label}</span>
        </CustomSelect.Option>
      ))}
    </CustomSelect>
  );
}

type TPendingMemberAction = {
  type: "ban" | "remove" | "unban";
  workspaceMember: TInstanceWorkspaceMember;
};

function MemberActionConfirmModal(props: {
  action: TPendingMemberAction | null;
  banDuration: string;
  isLoading: boolean;
  onBanDurationChange: (duration: string) => void;
  onClose: () => void;
  onConfirm: () => void;
}) {
  const { action, banDuration, isLoading, onBanDurationChange, onClose, onConfirm } = props;
  const memberName = action ? getMemberName(action.workspaceMember) : "";
  const title =
    action?.type === "ban"
      ? "Заблокировать участника"
      : action?.type === "unban"
        ? "Разблокировать участника"
        : "Удалить участника";
  const description =
    action?.type === "ban"
      ? `${memberName} не сможет открыть этот воркспейс и принять новые приглашения до снятия блокировки.`
      : action?.type === "unban"
        ? `${memberName} снова сможет войти в воркспейс. Проектные доступы после блокировки выдаются отдельно.`
        : `${memberName} будет удален из воркспейса и всех его проектов. Это крайний вариант удаления доступа.`;
  const confirmLabel = action?.type === "ban" ? "Заблокировать" : action?.type === "unban" ? "Разблокировать" : "Удалить";

  return (
    <Transition.Root show={Boolean(action)} as={Fragment}>
      <Dialog as="div" className="relative z-50" onClose={isLoading ? () => undefined : onClose}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-200"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-150"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-black/45 backdrop-blur-xl" />
        </Transition.Child>
        <div className="fixed inset-0 z-10 overflow-y-auto">
          <div className="flex min-h-full items-center justify-center p-4">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-200"
              enterFrom="translate-y-2 opacity-0 scale-95"
              enterTo="translate-y-0 opacity-100 scale-100"
              leave="ease-in duration-150"
              leaveFrom="translate-y-0 opacity-100 scale-100"
              leaveTo="translate-y-2 opacity-0 scale-95"
            >
              <Dialog.Panel className="nodedc-glass-modal nodedc-technical-confirm-modal w-full max-w-[33rem] rounded-[1.75rem] p-6 text-left">
                <Dialog.Title as="h3" className="text-17 font-semibold text-primary">
                  {title}
                </Dialog.Title>
                <p className="mt-2 text-13 leading-5 text-secondary">{description}</p>

                {action?.type === "ban" && (
                  <div className="mt-5">
                    <div className="mb-2 text-12 font-medium text-tertiary">Срок блокировки</div>
                    <CustomSelect
                      value={banDuration}
                      label={BAN_DURATION_OPTIONS.find((option) => option.value === banDuration)?.label}
                      onChange={(duration: string) => onBanDurationChange(duration)}
                      buttonClassName="nodedc-settings-select h-11 min-h-11 w-full justify-between px-4 text-13"
                      className="w-full"
                      disabled={isLoading}
                      input
                      maxHeight="sm"
                      optionsClassName="z-[90]"
                    >
                      {BAN_DURATION_OPTIONS.map((option) => (
                        <CustomSelect.Option key={option.value} value={option.value} className="w-full">
                          {option.label}
                        </CustomSelect.Option>
                      ))}
                    </CustomSelect>
                  </div>
                )}

                <div className="mt-6 flex justify-end gap-2.5">
                  <Button
                    variant="primary"
                    size="lg"
                    onClick={onClose}
                    disabled={isLoading}
                    className="nodedc-settings-save-button min-w-[8.5rem] justify-center"
                  >
                    Отмена
                  </Button>
                  <Button
                    variant="secondary"
                    size="lg"
                    onClick={onConfirm}
                    loading={isLoading}
                    className="nodedc-settings-secondary-button min-w-[8.5rem] justify-center"
                  >
                    {confirmLabel}
                  </Button>
                </div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  );
}

function WorkspaceModalShell(props: TWorkspaceAdminModalProps & { children: ReactNode; title: string; icon: ReactNode }) {
  const { children, icon, isOpen, onClose, title, workspaceId } = props;
  const { getWorkspaceById } = useWorkspace();
  const workspace = workspaceId ? getWorkspaceById(workspaceId) : undefined;

  return (
    <Transition.Root show={isOpen} as={Fragment}>
      <Dialog as="div" className="relative z-40" onClose={onClose}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-200"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-150"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-black/70 backdrop-blur-sm" />
        </Transition.Child>

        <div className="fixed inset-0 overflow-y-auto">
          <div className="flex min-h-full items-center justify-center p-4">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-200"
              enterFrom="translate-y-2 opacity-0 scale-95"
              enterTo="translate-y-0 opacity-100 scale-100"
              leave="ease-in duration-150"
              leaveFrom="translate-y-0 opacity-100 scale-100"
              leaveTo="translate-y-2 opacity-0 scale-95"
            >
              <Dialog.Panel className="nodedc-glass-modal relative w-full max-w-[80rem] overflow-hidden rounded-[1.75rem] bg-surface-1 text-left shadow-raised-200 transition-all">
                <div className="flex items-start justify-between gap-4 p-5 pr-16">
                  <div className="flex min-w-0 items-start gap-3">
                    <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl bg-layer-2 text-accent-primary">
                      {icon}
                    </span>
                    <div className="min-w-0">
                      <Dialog.Title as="h3" className="truncate text-18 font-semibold text-primary">
                        {title}
                      </Dialog.Title>
                      <div className="mt-1 truncate text-13 text-secondary">
                        {workspace ? `${workspace.name} / [${workspace.slug}]` : "Воркспейс"}
                      </div>
                    </div>
                  </div>
                  <button
                    type="button"
                    onClick={onClose}
                    className="absolute top-2 right-2 flex h-10 min-h-10 w-10 shrink-0 items-center justify-center rounded-full bg-white/6 p-0 text-primary transition hover:bg-white/10 hover:text-primary focus-visible:outline-none"
                    aria-label="Закрыть"
                  >
                    <X className="h-5 w-5 stroke-[2.2]" />
                  </button>
                </div>
                <div className="max-h-[72vh] overflow-y-auto p-5">{children}</div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  );
}

export function WorkspaceMembersModal(props: TWorkspaceAdminModalProps) {
  const { isOpen, workspaceId } = props;
  const [banDuration, setBanDuration] = useState("7d");
  const [mutatingMemberId, setMutatingMemberId] = useState<string | null>(null);
  const [pendingAction, setPendingAction] = useState<TPendingMemberAction | null>(null);
  const { data, isLoading, mutate } = useSWR(
    isOpen && workspaceId ? ["INSTANCE_WORKSPACE_MEMBERS", workspaceId] : null,
    () => instanceWorkspaceService.listMembers(workspaceId as string)
  );

  const handleRoleChange = async (workspaceMemberId: string, role: number) => {
    if (!workspaceId) return;
    setMutatingMemberId(workspaceMemberId);
    try {
      await instanceWorkspaceService.updateMemberRole(workspaceId, workspaceMemberId, role);
      await mutate();
      setToast({ type: TOAST_TYPE.SUCCESS, title: "Роль участника обновлена" });
    } catch (error) {
      setToast({
        type: TOAST_TYPE.ERROR,
        title: "Не удалось обновить роль",
        message: getErrorMessage(error, "Проверьте ограничения по администраторам воркспейса и проектов."),
      });
    } finally {
      setMutatingMemberId(null);
    }
  };

  const handleMemberBan = async (workspaceMember: TInstanceWorkspaceMember, isBanned: boolean) => {
    if (!workspaceId) return;

    setMutatingMemberId(workspaceMember.id);
    try {
      await instanceWorkspaceService.updateMemberBan(workspaceId, workspaceMember.id, {
        is_banned: isBanned,
        banned_until: isBanned ? getBanUntilIso(banDuration) : null,
      });
      await mutate();
      setToast({ type: TOAST_TYPE.SUCCESS, title: isBanned ? "Участник заблокирован" : "Участник разблокирован" });
    } catch (error) {
      setToast({
        type: TOAST_TYPE.ERROR,
        title: isBanned ? "Не удалось заблокировать участника" : "Не удалось разблокировать участника",
        message: getErrorMessage(error, "Пользователь может быть единственным администратором."),
      });
    } finally {
      setMutatingMemberId(null);
      setPendingAction(null);
    }
  };

  const handleRemove = async (workspaceMember: TInstanceWorkspaceMember) => {
    if (!workspaceId) return;

    setMutatingMemberId(workspaceMember.id);
    try {
      await instanceWorkspaceService.removeMember(workspaceId, workspaceMember.id);
      await mutate();
      setToast({ type: TOAST_TYPE.SUCCESS, title: "Участник удален из воркспейса" });
    } catch (error) {
      setToast({
        type: TOAST_TYPE.ERROR,
        title: "Не удалось удалить участника",
        message: getErrorMessage(error, "Пользователь может быть единственным администратором."),
      });
    } finally {
      setMutatingMemberId(null);
      setPendingAction(null);
    }
  };

  const handleConfirmAction = () => {
    if (!pendingAction) return;

    if (pendingAction.type === "ban") return handleMemberBan(pendingAction.workspaceMember, true);
    if (pendingAction.type === "unban") return handleMemberBan(pendingAction.workspaceMember, false);
    return handleRemove(pendingAction.workspaceMember);
  };

  return (
    <WorkspaceModalShell {...props} title="Участники воркспейса" icon={<UsersRound className="h-5 w-5" />}>
      <MemberActionConfirmModal
        action={pendingAction}
        banDuration={banDuration}
        isLoading={Boolean(mutatingMemberId)}
        onBanDurationChange={setBanDuration}
        onClose={() => setPendingAction(null)}
        onConfirm={handleConfirmAction}
      />
      {isLoading ? (
        <div className="flex min-h-40 items-center justify-center text-secondary">
          <Loader className="h-5 w-5 animate-spin" />
        </div>
      ) : (
        <div className="overflow-hidden rounded-[1.35rem] bg-layer-1">
          <div className="grid grid-cols-[minmax(14rem,1.45fr)_13rem_6rem_8rem_20rem] gap-3 border-b border-subtle px-4 py-3 text-11 font-semibold uppercase text-tertiary">
            <div>Пользователь</div>
            <div>Роль</div>
            <div>Проекты</div>
            <div>Админ проектов</div>
            <div className="text-right">Доступ</div>
          </div>
          <div className="divide-y divide-subtle">
            {(data ?? []).map((workspaceMember) => (
              <div
                key={workspaceMember.id}
                className="grid grid-cols-[minmax(14rem,1.45fr)_13rem_6rem_8rem_20rem] items-center gap-3 px-4 py-3 text-13"
              >
                <div className="min-w-0">
                  <div className="flex min-w-0 items-center gap-2">
                    <div className="truncate font-medium text-primary">{getMemberName(workspaceMember)}</div>
                    {workspaceMember.is_banned && (
                      <span className="shrink-0 rounded-full bg-red-500/12 px-2 py-0.5 text-10 font-semibold uppercase text-red-200">
                        Блокировка {formatBanUntil(workspaceMember.banned_until)}
                      </span>
                    )}
                  </div>
                  <div className="truncate text-12 text-tertiary">{workspaceMember.member.email}</div>
                </div>
                <RoleSelect
                  value={Number(workspaceMember.role)}
                  disabled={mutatingMemberId === workspaceMember.id}
                  onChange={(role) => handleRoleChange(workspaceMember.id, role)}
                />
                <div className="text-secondary">{workspaceMember.active_project_count}</div>
                <div className="text-secondary">{workspaceMember.admin_project_count}</div>
                <div className="flex justify-end gap-2">
                  <button
                    type="button"
                    onClick={() => setPendingAction({ type: workspaceMember.is_banned ? "unban" : "ban", workspaceMember })}
                    disabled={mutatingMemberId === workspaceMember.id}
                    className="nodedc-settings-secondary-button flex h-9 min-h-9 min-w-[9.5rem] items-center justify-center gap-2 whitespace-nowrap px-3 text-12"
                    aria-label={`${workspaceMember.is_banned ? "Разблокировать" : "Заблокировать"}: ${getMemberName(workspaceMember)}`}
                  >
                    {mutatingMemberId === workspaceMember.id ? (
                      <Loader className="h-3.5 w-3.5 animate-spin" />
                    ) : workspaceMember.is_banned ? (
                      <CheckCircle2 className="h-3.5 w-3.5" />
                    ) : (
                      <Ban className="h-3.5 w-3.5" />
                    )}
                    {workspaceMember.is_banned ? "Разблокировать" : "Заблокировать"}
                  </button>
                  <button
                    type="button"
                    onClick={() => setPendingAction({ type: "remove", workspaceMember })}
                    disabled={mutatingMemberId === workspaceMember.id}
                    className="nodedc-settings-secondary-button flex h-9 min-h-9 min-w-[7.5rem] items-center justify-center gap-2 whitespace-nowrap px-3 text-12"
                    aria-label={`Удалить участника: ${getMemberName(workspaceMember)}`}
                  >
                    {mutatingMemberId === workspaceMember.id ? (
                      <Loader className="h-3.5 w-3.5 animate-spin" />
                    ) : (
                      <Trash2 className="h-3.5 w-3.5" />
                    )}
                    Удалить
                  </button>
                </div>
              </div>
            ))}
          </div>
          {(data ?? []).length === 0 && <div className="px-4 py-8 text-center text-13 text-tertiary">Активных участников нет</div>}
        </div>
      )}
    </WorkspaceModalShell>
  );
}

export function WorkspaceFeaturesModal(props: TWorkspaceAdminModalProps) {
  const { isOpen, workspaceId } = props;
  const [mutatingFeatureKey, setMutatingFeatureKey] = useState<string | null>(null);
  const { data, isLoading, mutate } = useSWR(
    isOpen && workspaceId ? ["INSTANCE_WORKSPACE_FEATURES", workspaceId] : null,
    () => instanceWorkspaceService.retrieveFeatures(workspaceId as string)
  );

  const handleToggle = async (feature: TInstanceWorkspaceFeature) => {
    if (!workspaceId) return;
    setMutatingFeatureKey(feature.key);
    try {
      await instanceWorkspaceService.updateFeature(workspaceId, feature.key, !feature.is_enabled);
      await mutate();
      setToast({
        type: TOAST_TYPE.SUCCESS,
        title: feature.is_enabled ? "Фича отключена для воркспейса" : "Фича выдана воркспейсу",
      });
    } catch (error) {
      setToast({
        type: TOAST_TYPE.ERROR,
        title: "Не удалось обновить доступ",
        message: getErrorMessage(error, "Попробуйте еще раз."),
      });
    } finally {
      setMutatingFeatureKey(null);
    }
  };

  return (
    <WorkspaceModalShell {...props} title="Функции воркспейса" icon={<ShieldCheck className="h-5 w-5" />}>
      {isLoading ? (
        <div className="flex min-h-40 items-center justify-center text-secondary">
          <Loader className="h-5 w-5 animate-spin" />
        </div>
      ) : (
        <div className="space-y-3">
          {(data?.features ?? []).map((feature) => (
            <div key={feature.key} className="nodedc-settings-card flex items-center justify-between gap-5 p-4">
              <div className="min-w-0">
                <div className="flex flex-wrap items-center gap-2">
                  <div className="text-15 font-semibold text-primary">{feature.title}</div>
                  <span
                    className={cn(
                      "rounded-full px-2 py-0.5 text-11 font-medium",
                      feature.is_enabled ? "bg-accent-primary text-on-color" : "bg-layer-2 text-tertiary"
                    )}
                  >
                    {feature.is_enabled ? "Доступ выдан" : "Не выдано"}
                  </span>
                </div>
                <div className="mt-1 text-13 text-secondary">{feature.description}</div>
                <div className="mt-3 flex flex-wrap gap-2 text-11 text-tertiary">
                  <span className="rounded-full bg-layer-2 px-2 py-1">
                    Workspace: {feature.workspace_setting_enabled ? "включено" : "выключено"}
                  </span>
                  <span className="rounded-full bg-layer-2 px-2 py-1">Доступ: {ACCESS_MODE_LABELS[feature.access_mode]}</span>
                  <span className="rounded-full bg-layer-2 px-2 py-1">
                    OpenAI key: {feature.has_workspace_key ? "есть" : "нет"}
                  </span>
                </div>
              </div>
              <div className="shrink-0">
                <ToggleSwitch
                  value={feature.is_enabled}
                  onChange={() => handleToggle(feature)}
                  size="sm"
                  disabled={mutatingFeatureKey === feature.key}
                />
              </div>
            </div>
          ))}
          {(data?.features ?? []).length === 0 && <div className="px-4 py-8 text-center text-13 text-tertiary">Функции не найдены</div>}
        </div>
      )}
      <div className="mt-5 flex justify-end">
        <Button variant="secondary" size="lg" onClick={props.onClose} className="nodedc-settings-secondary-button">
          Закрыть
        </Button>
      </div>
    </WorkspaceModalShell>
  );
}
