import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { Client } from "../entities/client/types";
import type { Invite } from "../entities/invite/types";
import { syncServiceLaunchLink } from "../entities/service/links";
import type { LauncherServiceView, Service } from "../entities/service/types";
import type { ClientGroup, ClientMembership, LauncherUser } from "../entities/user/types";
import {
  approveAdminAccessRequest,
  approveAdminEngineWorkflowAccessRequest,
  approveAdminTaskerInviteRequest,
  createAdminClient,
  createAdminGroup,
  createAdminInvite,
  createAdminService,
  createAdminUser,
  deleteAdminClient,
  deleteAdminGroup,
  deleteAdminInvite,
  deleteAdminMembership,
  deleteAdminService,
  deleteAdminUser,
  ensureAdminTaskManagerProjectMembership,
  ensureAdminTaskManagerWorkspaceMembership,
  fetchAdminTaskManagerWorkspaces,
  reorderAdminServices,
  retryAdminSync,
  rejectAdminAccessRequest,
  rejectAdminEngineWorkflowAccessRequest,
  rejectAdminTaskerInviteRequest,
  removeAdminTaskManagerProjectMembership,
  removeAdminTaskManagerWorkspaceMembership,
  setAdminServiceModuleEntitlement,
  setAdminUserServiceAccess,
  updateAdminClient,
  updateAdminAccessRequest,
  updateAdminGroup,
  updateAdminInvite,
  updateAdminMembership,
  updateAdminService,
  updateAdminSettings,
  updateAdminUserProfile,
  type ControlPlaneMutationResult,
  type TaskManagerWorkspaceMemberRole,
  type TaskManagerWorkspaceSummary,
} from "../shared/api/adminApi";
import { createAccessRequest, type CreateAccessRequestResponse } from "../shared/api/accessRequestApi";
import {
  buildLauncherServices,
  buildMe,
  initialLauncherData,
  normalizeLauncherData,
  profileOptions,
  type LauncherData,
  type LauncherSettings,
} from "../shared/api/mockApi";
import {
  fetchAuthSession,
  fetchAvailableApps,
  type AuthenticatedSession,
  type AuthSession,
  type LauncherAuthApp,
} from "../shared/api/authApi";
import { updateOwnPassword, updateOwnProfile } from "../shared/api/profileApi";
import { acceptInvite, fetchPublicInvite, registerInvite, type PublicInviteResponse, type RegisterInviteCommand } from "../shared/api/inviteApi";
import { fetchNotifications, markAllNotificationsRead, markNotificationRead, type NotificationDelivery } from "../shared/api/notificationsApi";
import type { CreateAccessRequestCommand } from "../entities/access-request/types";
import { subscribeToNodeDCLogoutEvents } from "../shared/session/sessionSync";
import { loadPersistedLauncherData } from "../shared/api/storageApi";
import type {
  AccessAssignmentValue,
  CreateUserCommand,
  EnsureTaskManagerProjectMemberCommand,
  SetServiceModuleEntitlementCommand,
  SetUserServiceAccessCommand,
} from "../widgets/admin-overlay/AdminOverlay";
import { ServiceRail } from "../widgets/service-rail/ServiceRail";
import { ServiceStage } from "../widgets/service-stage/ServiceStage";
import { TopBar, type LauncherAdminMode, type LauncherNotificationItem } from "../widgets/top-bar/TopBar";

let lastAuthRedirect: { url: string; startedAt: number } | null = null;

const AdminOverlay = lazy(() => import("../widgets/admin-overlay/AdminOverlay").then((module) => ({ default: module.AdminOverlay })));
const ProfileSettingsPanel = lazy(() =>
  import("../widgets/profile-settings-panel/ProfileSettingsPanel").then((module) => ({ default: module.ProfileSettingsPanel }))
);

type InviteFlowState =
  | { status: "loading" }
  | { status: "ready"; payload: PublicInviteResponse }
  | { status: "accepting"; payload: PublicInviteResponse }
  | { status: "accepted"; payload: PublicInviteResponse }
  | { status: "registering"; payload: PublicInviteResponse }
  | { status: "registered"; payload: PublicInviteResponse; loginUrl: string }
  | { status: "error"; message: string; payload?: PublicInviteResponse };

type ControlPlaneMutationOutcome = { ok: true; data: LauncherData } | { ok: false; message: string };

export function LauncherApp() {
  const inviteToken = useMemo(() => parseInviteToken(window.location.pathname), []);
  const isAccessRequestRoute = useMemo(() => isAccessRequestPath(window.location.pathname), []);
  const [data, setData] = useState<LauncherData>(() => syncLauncherServiceLinks(initialLauncherData));
  const [activeProfileId, setActiveProfileId] = useState(profileOptions[0].userId);
  const [activeClientId, setActiveClientId] = useState(profileOptions[0].defaultClientId);
  const [selectedServiceId, setSelectedServiceId] = useState<string | undefined>();
  const [adminOpen, setAdminOpen] = useState(false);
  const [adminMode, setAdminMode] = useState<LauncherAdminMode>("admin");
  const [adminTemporarilyHidden, setAdminTemporarilyHidden] = useState(false);
  const [authSession, setAuthSession] = useState<AuthSession | null>(null);
  const [authApps, setAuthApps] = useState<LauncherAuthApp[] | null>(null);
  const [runtimeReady, setRuntimeReady] = useState(false);
  const [profileSettingsOpen, setProfileSettingsOpen] = useState(false);
  const [pendingAccessAssignments, setPendingAccessAssignments] = useState<Record<string, AccessAssignmentValue>>({});
  const [pendingServiceModuleEntitlements, setPendingServiceModuleEntitlements] = useState<Record<string, boolean>>({});
  const [pendingTaskManagerMemberships, setPendingTaskManagerMemberships] = useState<Record<string, boolean>>({});
  const [pendingTaskManagerProjectMemberships, setPendingTaskManagerProjectMemberships] = useState<Record<string, boolean>>({});
  const [taskManagerWorkspaces, setTaskManagerWorkspaces] = useState<TaskManagerWorkspaceSummary[]>([]);
  const [taskManagerWorkspacesLoading, setTaskManagerWorkspacesLoading] = useState(false);
  const [taskManagerWorkspacesError, setTaskManagerWorkspacesError] = useState<string | null>(null);
  const [hubNotifications, setHubNotifications] = useState<LauncherNotificationItem[]>([]);
  const [inviteFlow, setInviteFlow] = useState<InviteFlowState | null>(() => (inviteToken ? { status: "loading" } : null));
  const runtimeDataRef = useRef(data);
  const runtimeProfileIdRef = useRef(activeProfileId);
  const runtimeClientIdRef = useRef(activeClientId);
  const runtimeRefreshInFlightRef = useRef<Promise<void> | null>(null);
  const resolvedProfileId = useMemo(
    () => resolveRuntimeProfileId(data, authSession, activeProfileId),
    [activeProfileId, authSession, data]
  );

  useEffect(() => {
    runtimeDataRef.current = data;
    runtimeProfileIdRef.current = resolvedProfileId;
    runtimeClientIdRef.current = activeClientId;
  }, [activeClientId, data, resolvedProfileId]);

  const me = useMemo(() => buildMe(data, resolvedProfileId, activeClientId), [data, resolvedProfileId, activeClientId]);
  const activeProfileUser = data.users.find((user) => user.id === resolvedProfileId) ?? data.users[0];
  const runtimeMe = useMemo(() => {
    if (!authSession?.authenticated) return me;

    return {
      ...me,
      user: {
        ...me.user,
        authentikUserId: authSession.user.sub,
        email: me.user.email || authSession.user.email,
        name: me.user.name || authSession.user.name,
        avatarUrl: me.user.avatarUrl ?? authSession.user.avatarUrl,
      },
      mockAuthentikClaims: {
        ...me.mockAuthentikClaims,
        sub: authSession.user.sub,
        email: authSession.user.email || me.mockAuthentikClaims.email,
        name: authSession.user.name || me.mockAuthentikClaims.name,
        avatarUrl: authSession.user.avatarUrl ?? null,
        groups: authSession.groups,
      },
    };
  }, [authSession, me]);
  const resolvedClientId = me.activeClientId;
  const canOpenAdminApi = Boolean(authSession?.authenticated && runtimeMe.permissions.canOpenAdmin);
  const authAppsBySlug = useMemo(() => new Map((authApps ?? []).map((app) => [app.slug, app])), [authApps]);
  const launcherServices = useMemo(
    () => {
      const services = buildLauncherServices(data, resolvedProfileId, resolvedClientId);

      if (!authSession?.authenticated || authApps === null) {
        return [];
      }

      return services.map((service) => {
        const app = authAppsBySlug.get(service.slug);

        if (!app) {
          return {
            ...service,
            userAccess: "denied" as const,
            openUrl: null,
            effectiveAccess: {
              ...service.effectiveAccess,
              allowed: false,
              visible: true,
              openEnabled: false,
              reason: "Нет доступа",
            },
          };
        }

        const appVisible = app.status !== "hidden" && app.status !== "disabled";
        const allowed = app.hasAccess && appVisible && service.effectiveAccess.allowed;
        const openEnabled = allowed && app.status === "active" && service.effectiveAccess.openEnabled;

        return {
          ...service,
          title: app.title || service.title,
          description: app.description || service.description,
          openUrl: openEnabled ? app.openUrl || app.url || service.openUrl : null,
          userAccess: allowed ? ("allowed" as const) : ("denied" as const),
          effectiveAccess: {
            ...service.effectiveAccess,
            allowed,
            visible: appVisible,
            openEnabled,
            reason: !app.hasAccess ? app.accessReason || "Нет доступа" : service.effectiveAccess.reason,
          },
        };
      }).filter((service) => service.effectiveAccess.visible);
    },
    [authApps, authAppsBySlug, authSession, data, resolvedProfileId, resolvedClientId]
  );

  useEffect(() => {
    if (!launcherServices.length) {
      setSelectedServiceId(undefined);
      return;
    }

    if (selectedServiceId && !launcherServices.some((service) => service.id === selectedServiceId)) {
      setSelectedServiceId(undefined);
    }
  }, [launcherServices, selectedServiceId]);

  const selectedService = launcherServices.find((service) => service.id === selectedServiceId);

  const refreshRuntimeState = useCallback(async () => {
    if (runtimeRefreshInFlightRef.current) {
      return runtimeRefreshInFlightRef.current;
    }

    const request = (async () => {
      try {
        const nextSession = await fetchAuthSession();

        setAuthSession(nextSession);

        if (!nextSession.authenticated) {
          setAuthApps([]);
          setRuntimeReady(true);
          return;
        }

        const [persistedData, apps] = await Promise.all([
          loadPersistedLauncherData(),
          fetchAvailableApps(),
        ]);

        if (persistedData) {
          setData(syncLauncherServiceLinks(persistedData));
        }

        setAuthApps(apps);
        setRuntimeReady(true);
      } catch (error: unknown) {
        setRuntimeReady(true);
        console.warn(error instanceof Error ? error.message : "Не удалось обновить runtime состояние Launcher");
      }
    })().finally(() => {
      runtimeRefreshInFlightRef.current = null;
    });

    runtimeRefreshInFlightRef.current = request;
    return request;
  }, []);

  const loadHubNotifications = useCallback(async () => {
    if (!authSession?.authenticated) {
      setHubNotifications([]);
      return;
    }

    try {
      const deliveries = await fetchNotifications("hub");
      setHubNotifications(deliveries.map(mapNotificationDeliveryToLauncherItem));
    } catch (error: unknown) {
      setHubNotifications([]);
      console.warn(error instanceof Error ? error.message : "Не удалось загрузить уведомления Hub");
    }
  }, [authSession?.authenticated]);

  const markHubNotificationsRead = useCallback(async () => {
    try {
      await markAllNotificationsRead("hub");
      await loadHubNotifications();
    } catch (error: unknown) {
      console.warn(error instanceof Error ? error.message : "Не удалось отметить уведомления Hub прочитанными");
    }
  }, [loadHubNotifications]);

  useEffect(() => {
    let isMounted = true;

    refreshRuntimeState()
      .catch((error: unknown) => {
        if (!isMounted) return;

        setAuthSession({ authenticated: false, loginUrl: "/auth/login" });
        setAuthApps([]);
        setRuntimeReady(true);
        console.warn(error instanceof Error ? error.message : "Не удалось проверить сессию платформы");
      });

    return () => {
      isMounted = false;
    };
  }, [refreshRuntimeState]);

  useEffect(() => {
    if (!authSession || authSession.authenticated) return;
    if (inviteToken || isAccessRequestRoute) return;

    redirectToLogin(authSession.loginUrl);
  }, [authSession, inviteToken, isAccessRequestRoute]);

  useEffect(() => {
    if (!inviteToken) return;

    let isMounted = true;
    setInviteFlow({ status: "loading" });

    fetchPublicInvite(inviteToken)
      .then((payload) => {
        if (!isMounted) return;
        setInviteFlow({ status: "ready", payload });
      })
      .catch((error: unknown) => {
        if (!isMounted) return;
        setInviteFlow({ status: "error", message: error instanceof Error ? error.message : "Инвайт не найден" });
      });

    return () => {
      isMounted = false;
    };
  }, [inviteToken]);

  useEffect(() => {
    let isRedirecting = false;

    return subscribeToNodeDCLogoutEvents(() => {
      if (isRedirecting) return;

      isRedirecting = true;
      redirectToLogin("/auth/login");
    });
  }, []);

  useEffect(() => {
    let isMounted = true;

    const validateRestoredSession = (event: PageTransitionEvent) => {
      if (!event.persisted) return;

      fetchAuthSession()
        .then((session) => {
          if (!isMounted) return;

          if (!session.authenticated) {
            if (inviteToken || isAccessRequestRoute) return;
            redirectToLogin(session.loginUrl);
            return;
          }

          setAuthSession(session);
        })
        .catch(() => {
          if (isMounted && !inviteToken && !isAccessRequestRoute) {
            redirectToLogin("/auth/login");
          }
        });
    };

    window.addEventListener("pageshow", validateRestoredSession);

    return () => {
      isMounted = false;
      window.removeEventListener("pageshow", validateRestoredSession);
    };
  }, [inviteToken, isAccessRequestRoute]);

  useEffect(() => {
    if (!authSession?.authenticated) return;

    const nextContext = resolveAuthenticatedContext(data, authSession, activeProfileId, activeClientId);

    if (activeProfileId !== nextContext.profileId) {
      setActiveProfileId(nextContext.profileId);
    }

    if (activeClientId !== nextContext.clientId) {
      setActiveClientId(nextContext.clientId);
    }
  }, [activeClientId, activeProfileId, authSession, data]);

  useEffect(() => {
    if (!adminOpen || !canOpenAdminApi) return;
    void refreshTaskManagerWorkspaces();
  }, [adminOpen, canOpenAdminApi]);

  useEffect(() => {
    if (runtimeMe.permissions.canOpenAdmin) return;

    setAdminTemporarilyHidden(false);
    setAdminOpen(false);
    setAdminMode("admin");
  }, [runtimeMe.permissions.canOpenAdmin]);

  useEffect(() => {
    if (!authSession?.authenticated) return;

    let isMounted = true;

    const refreshMountedRuntimeState = async () => {
      await refreshRuntimeState();
      if (!isMounted) return;
    };

    const eventSource = new EventSource("/api/events");

    eventSource.addEventListener("nodedc-runtime", () => {
      void refreshMountedRuntimeState();
    });

    eventSource.onerror = () => {
      console.warn("Launcher event stream disconnected; browser will retry automatically");
    };

    return () => {
      isMounted = false;
      eventSource.close();
    };
  }, [authSession?.authenticated, refreshRuntimeState]);

  useEffect(() => {
    if (!authSession?.authenticated) {
      setHubNotifications([]);
      return;
    }

    void loadHubNotifications();

    const eventSource = new EventSource("/api/notifications/stream?surface=hub");
    const refreshHubNotifications = () => {
      void loadHubNotifications();
    };

    eventSource.addEventListener("notification.ready", refreshHubNotifications);
    eventSource.addEventListener("notification.delivery.created", refreshHubNotifications);
    eventSource.addEventListener("notification.delivery.read", refreshHubNotifications);
    eventSource.addEventListener("notification.deliveries.read-all", refreshHubNotifications);
    eventSource.onerror = () => {
      console.warn("Hub notification stream disconnected; browser will retry automatically");
    };

    return () => {
      eventSource.close();
    };
  }, [authSession?.authenticated, loadHubNotifications]);

  useEffect(() => {
    if (!authSession?.authenticated) return;

    const refreshVisibleRuntimeState = () => {
      if (document.visibilityState === "visible") {
        void refreshRuntimeState();
      }
    };

    window.addEventListener("focus", refreshVisibleRuntimeState);
    document.addEventListener("visibilitychange", refreshVisibleRuntimeState);

    return () => {
      window.removeEventListener("focus", refreshVisibleRuntimeState);
      document.removeEventListener("visibilitychange", refreshVisibleRuntimeState);
    };
  }, [authSession?.authenticated, refreshRuntimeState]);

  useEffect(() => {
    if (!authSession?.authenticated) return;

    const intervalId = window.setInterval(() => {
      if (document.visibilityState === "visible") {
        void refreshRuntimeState();
      }
    }, 60000);

    return () => {
      window.clearInterval(intervalId);
    };
  }, [authSession?.authenticated, refreshRuntimeState]);

  function handleProfileChange(userId: string) {
    const profile = profileOptions.find((option) => option.userId === userId);
    setActiveProfileId(userId);
    setActiveClientId(profile?.defaultClientId ?? activeClientId);
    setAdminTemporarilyHidden(false);
    setAdminOpen(false);
  }

  function handleLaunch(service: LauncherServiceView) {
    if (!service.openUrl || !service.effectiveAccess.openEnabled) return;
    window.open(service.openUrl, "_blank", "noopener,noreferrer");
  }

  function handleServiceSelect(serviceId: string) {
    if (selectedServiceId === serviceId) {
      setSelectedServiceId(undefined);
      setAdminTemporarilyHidden(false);
      return;
    }

    if (adminOpen) {
      setAdminTemporarilyHidden(true);
    }

    setSelectedServiceId(serviceId);
  }

  function handleStageStep(direction: "previous" | "next") {
    if (!launcherServices.length) return;

    setSelectedServiceId((current) => {
      const currentIndex = current ? launcherServices.findIndex((service) => service.id === current) : -1;
      const fallbackIndex = direction === "next" ? 0 : launcherServices.length - 1;
      const nextIndex =
        currentIndex === -1
          ? fallbackIndex
          : direction === "next"
            ? (currentIndex + 1) % launcherServices.length
            : (currentIndex - 1 + launcherServices.length) % launcherServices.length;

      return launcherServices[nextIndex]?.id;
    });
  }

  async function applyControlPlaneMutation(request: Promise<ControlPlaneMutationResult>): Promise<ControlPlaneMutationOutcome> {
    try {
      const result = await request;
      setData(syncLauncherServiceLinks(result.data));
      return { ok: true, data: result.data };
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : "Не удалось выполнить admin API операцию";
      console.warn(message);
      return { ok: false, message };
    }
  }

  async function refreshTaskManagerWorkspaces() {
    setTaskManagerWorkspacesLoading(true);
    setTaskManagerWorkspacesError(null);

    try {
      const result = await fetchAdminTaskManagerWorkspaces();
      setTaskManagerWorkspaces(result.workspaces ?? []);
    } catch (error: unknown) {
      setTaskManagerWorkspacesError(error instanceof Error ? error.message : "Не удалось загрузить workspace Operational Core");
    } finally {
      setTaskManagerWorkspacesLoading(false);
    }
  }

  function handleSetUserServiceAccess({ userId, serviceId, value }: SetUserServiceAccessCommand) {
    const assignmentKey = accessAssignmentKey(userId, serviceId);

    if (pendingAccessAssignments[assignmentKey]) {
      return;
    }

    setPendingAccessAssignments((current) => ({ ...current, [assignmentKey]: value }));
    setAdminUserServiceAccess({ userId, serviceId, value })
      .then((result) => {
        setData(syncLauncherServiceLinks(result.data));
      })
      .catch((error: unknown) => {
        console.warn(error instanceof Error ? error.message : "Не удалось выполнить admin API операцию");
      })
      .finally(() => {
        setPendingAccessAssignments((current) => {
          const { [assignmentKey]: _completed, ...rest } = current;
          return rest;
        });
      });
  }

  function handleSetServiceModuleEntitlement(command: SetServiceModuleEntitlementCommand) {
    const entitlementKey = `${command.userId}:${command.serviceId}:${command.moduleId}`;

    if (pendingServiceModuleEntitlements[entitlementKey]) {
      return;
    }

    setPendingServiceModuleEntitlements((current) => ({ ...current, [entitlementKey]: true }));
    setAdminServiceModuleEntitlement(command)
      .then((result) => {
        setData(syncLauncherServiceLinks(result.data));
      })
      .catch((error: unknown) => {
        console.warn(error instanceof Error ? error.message : "Не удалось обновить модуль сервиса");
      })
      .finally(() => {
        setPendingServiceModuleEntitlements((current) => {
          const { [entitlementKey]: _completed, ...rest } = current;
          return rest;
        });
      });
  }

  function handleSetTaskManagerWorkspaceMemberRole(command: { clientId: string; userId: string; role: TaskManagerWorkspaceMemberRole; workspaceSlug?: string }) {
    const membershipKey = `${command.clientId}:${command.userId}:${command.workspaceSlug ?? "primary"}`;

    if (pendingTaskManagerMemberships[membershipKey]) {
      return;
    }

    setPendingTaskManagerMemberships((current) => ({ ...current, [membershipKey]: true }));
    const request =
      command.role === "unset"
        ? removeAdminTaskManagerWorkspaceMembership({ clientId: command.clientId, userId: command.userId, workspaceSlug: command.workspaceSlug })
        : ensureAdminTaskManagerWorkspaceMembership({
            clientId: command.clientId,
            userId: command.userId,
            workspaceSlug: command.workspaceSlug,
            role: command.role,
            setLastWorkspace: true,
          });

    request
      .then((result) => {
        setData(syncLauncherServiceLinks(result.data));
      })
      .catch((error: unknown) => {
        console.warn(error instanceof Error ? error.message : "Не удалось синхронизировать Tasker");
      })
      .finally(() => {
        setPendingTaskManagerMemberships((current) => {
          const { [membershipKey]: _completed, ...rest } = current;
          return rest;
        });
      });
  }

  function handleSetTaskManagerProjectMemberRole(command: EnsureTaskManagerProjectMemberCommand) {
    const membershipKey = `${command.clientId}:${command.userId}:${command.workspaceSlug}:${command.projectId}`;

    if (pendingTaskManagerProjectMemberships[membershipKey]) {
      return;
    }

    setPendingTaskManagerProjectMemberships((current) => ({ ...current, [membershipKey]: true }));
    const request =
      command.role === "unset"
        ? removeAdminTaskManagerProjectMembership({
            clientId: command.clientId,
            userId: command.userId,
            workspaceSlug: command.workspaceSlug,
            projectId: command.projectId,
          })
        : ensureAdminTaskManagerProjectMembership({
            clientId: command.clientId,
            userId: command.userId,
            workspaceSlug: command.workspaceSlug,
            projectId: command.projectId,
            role: command.role,
          });

    request
      .then((result) => {
        setData(syncLauncherServiceLinks(result.data));
      })
      .catch((error: unknown) => {
        console.warn(error instanceof Error ? error.message : "Не удалось синхронизировать проект Tasker");
      })
      .finally(() => {
        setPendingTaskManagerProjectMemberships((current) => {
          const { [membershipKey]: _completed, ...rest } = current;
          return rest;
        });
      });
  }

  function handleCreateInvite(invite: Pick<Invite, "clientId" | "email" | "role">) {
    applyControlPlaneMutation(createAdminInvite(invite));
  }

  async function handleAcceptInvite() {
    if (!inviteToken || inviteFlow?.status !== "ready") return;

    setInviteFlow({ status: "accepting", payload: inviteFlow.payload });

    try {
      const result = await acceptInvite(inviteToken);
      setData(syncLauncherServiceLinks(result.data));
      if (result.redirectUrl && result.redirectUrl !== "/") {
        window.location.assign(result.redirectUrl);
        return;
      }
      setInviteFlow({ status: "accepted", payload: inviteFlow.payload });
    } catch (error) {
      setInviteFlow({
        status: "error",
        payload: inviteFlow.payload,
        message: error instanceof Error ? error.message : "Не удалось принять инвайт",
      });
    }
  }

  async function handleRegisterInvite(command: RegisterInviteCommand) {
    if (!inviteToken || !inviteFlow || !("payload" in inviteFlow) || !inviteFlow.payload || (inviteFlow.status !== "ready" && inviteFlow.status !== "error")) return;

    const payload = inviteFlow.payload;

    setInviteFlow({ status: "registering", payload });

    try {
      const result = await registerInvite(inviteToken, command);

      setData(syncLauncherServiceLinks(result.data));
      setInviteFlow({ status: "registered", payload, loginUrl: result.loginUrl });
      window.location.replace(result.redirectUrl || "/");
    } catch (error) {
      setInviteFlow({
        status: "error",
        message: error instanceof Error ? error.message : "Не удалось зарегистрироваться по инвайту",
        payload,
      });
    }
  }

  function handleUpdateInvite(inviteId: string, patch: Partial<Invite>) {
    applyControlPlaneMutation(updateAdminInvite(inviteId, patch));
  }

  function handleDeleteInvite(inviteId: string) {
    applyControlPlaneMutation(deleteAdminInvite(inviteId));
  }

  function handleUpdateAccessRequest(accessRequestId: string, patch: Parameters<typeof updateAdminAccessRequest>[1]) {
    applyControlPlaneMutation(updateAdminAccessRequest(accessRequestId, patch));
  }

  function handleApproveAccessRequest(accessRequestId: string, patch: Parameters<typeof approveAdminAccessRequest>[1]) {
    applyControlPlaneMutation(approveAdminAccessRequest(accessRequestId, patch));
  }

  function handleRejectAccessRequest(accessRequestId: string, patch: Parameters<typeof rejectAdminAccessRequest>[1]) {
    applyControlPlaneMutation(rejectAdminAccessRequest(accessRequestId, patch));
  }

  function handleApproveTaskerInviteRequest(
    taskerInviteRequestId: string,
    patch: Parameters<typeof approveAdminTaskerInviteRequest>[1]
  ) {
    applyControlPlaneMutation(approveAdminTaskerInviteRequest(taskerInviteRequestId, patch));
  }

  function handleRejectTaskerInviteRequest(
    taskerInviteRequestId: string,
    patch: Parameters<typeof rejectAdminTaskerInviteRequest>[1]
  ) {
    applyControlPlaneMutation(rejectAdminTaskerInviteRequest(taskerInviteRequestId, patch));
  }

  function handleApproveEngineWorkflowAccessRequest(
    engineWorkflowAccessRequestId: string,
    patch: Parameters<typeof approveAdminEngineWorkflowAccessRequest>[1]
  ) {
    return applyControlPlaneMutation(approveAdminEngineWorkflowAccessRequest(engineWorkflowAccessRequestId, patch));
  }

  function handleRejectEngineWorkflowAccessRequest(
    engineWorkflowAccessRequestId: string,
    patch: Parameters<typeof rejectAdminEngineWorkflowAccessRequest>[1]
  ) {
    return applyControlPlaneMutation(rejectAdminEngineWorkflowAccessRequest(engineWorkflowAccessRequestId, patch));
  }

  function handleRetrySync(syncId: string) {
    applyControlPlaneMutation(retryAdminSync(syncId));
  }

  function handleUpdateSettings(patch: Partial<LauncherSettings>) {
    return applyControlPlaneMutation(updateAdminSettings(patch));
  }

  function handleUpdateService(serviceId: string, patch: Partial<Service>) {
    return applyControlPlaneMutation(updateAdminService(serviceId, patch));
  }

  function handleCreateClient() {
    const index = data.clients.length + 1;

    applyControlPlaneMutation(
      createAdminClient({
        type: "company",
        name: `Новый клиент ${index}`,
        legalName: `Новый клиент ${index}`,
        status: "demo",
        demoEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(),
        contactName: "",
        contactEmail: "",
        notes: "",
      })
    );
  }

  function handleUpdateClient(clientId: string, patch: Partial<Client>) {
    applyControlPlaneMutation(updateAdminClient(clientId, patch));
  }

  function handleDeleteClient(clientId: string) {
    const nextClientId = data.clients.find((client) => client.id !== clientId)?.id ?? activeClientId;

    applyControlPlaneMutation(deleteAdminClient(clientId));

    if (activeClientId === clientId) {
      setActiveClientId(nextClientId);
    }
  }

  function handleUpdateUser(userId: string, patch: Partial<LauncherUser>) {
    applyControlPlaneMutation(updateAdminUserProfile(userId, patch));
  }

  function handleDeleteUser(userId: string) {
    applyControlPlaneMutation(deleteAdminUser(userId));
  }

  async function handleUpdateOwnProfile(patch: Partial<LauncherUser>) {
    const result = await updateOwnProfile(patch);
    setData(syncLauncherServiceLinks(result.data));
  }

  async function handleUpdateOwnPassword(newPassword: string) {
    const result = await updateOwnPassword(newPassword);
    setData(syncLauncherServiceLinks(result.data));
  }

  function handleCreateUser(command: CreateUserCommand) {
    createAdminUser(command)
      .then((result) => {
        setData(syncLauncherServiceLinks(result.data));

        if (result.provisioning?.temporaryPassword) {
          window.alert(`Пользователь создан. Временный пароль: ${result.provisioning.temporaryPassword}`);
        }
      })
      .catch((error: unknown) => {
        console.warn(error instanceof Error ? error.message : "Не удалось создать пользователя");
      });
  }

  function handleUpdateMembership(membershipId: string, patch: Partial<ClientMembership>) {
    applyControlPlaneMutation(updateAdminMembership(membershipId, patch));
  }

  function handleDeleteMembership(membershipId: string) {
    applyControlPlaneMutation(deleteAdminMembership(membershipId));
  }

  function handleCreateGroup(clientId: string) {
    applyControlPlaneMutation(createAdminGroup({ clientId, name: "Новая группа", description: "Описание группы", memberIds: [] }));
  }

  function handleUpdateGroup(groupId: string, patch: Partial<ClientGroup>) {
    applyControlPlaneMutation(updateAdminGroup(groupId, patch));
  }

  function handleDeleteGroup(groupId: string) {
    applyControlPlaneMutation(deleteAdminGroup(groupId));
  }

  function handleReorderServices(orderedServiceIds: string[]) {
    applyControlPlaneMutation(reorderAdminServices(orderedServiceIds));
  }

  function handleCreateService() {
    applyControlPlaneMutation(createAdminService());
  }

  function handleDeleteService(serviceId: string) {
    applyControlPlaneMutation(deleteAdminService(serviceId));

    setSelectedServiceId((current) => (current === serviceId ? undefined : current));
  }

  if (isAccessRequestRoute) {
    return (
      <AccessRequestScreen
        onSubmit={createAccessRequest}
        onLogin={() => redirectToLogin(authSession?.authenticated ? "/auth/login?prompt=login" : authSession?.loginUrl, { returnTo: "/" })}
      />
    );
  }

  if (inviteToken) {
    return (
      <InviteFlowScreen
        state={inviteFlow ?? { status: "loading" }}
        authenticatedEmail={authSession?.authenticated ? authSession.user.email : null}
        onAccept={() => void handleAcceptInvite()}
        onRegister={(command) => void handleRegisterInvite(command)}
        onLogin={() => redirectToLogin(authSession?.authenticated ? "/auth/login?prompt=login" : authSession?.loginUrl)}
        onSwitchAccount={() => {
          const returnTo = `${window.location.pathname}${window.location.search}${window.location.hash}`;
          window.location.replace(`/auth/logout?global=1&returnTo=${encodeURIComponent(returnTo)}`);
        }}
        onGoHome={() => {
          window.history.replaceState(null, "", "/");
          window.location.replace("/");
        }}
      />
    );
  }

  if (!authSession || (authSession.authenticated && !runtimeReady)) {
    return null;
  }

  if (!authSession.authenticated) {
    return null;
  }

  const handleLogout = () => {
    window.location.replace(authSession.logoutUrl);
  };

  return (
    <div className="launcher-app">
      <TopBar
        me={runtimeMe}
        clients={data.clients}
        profileOptions={profileOptions}
        activeProfileId={resolvedProfileId}
        activeClientId={resolvedClientId}
        adminOpen={adminOpen && !adminTemporarilyHidden}
        adminMode={runtimeMe.launcherRole === "root_admin" ? adminMode : "admin"}
        onProfileChange={handleProfileChange}
        onClientChange={setActiveClientId}
        onOpenAdmin={() => {
          setAdminTemporarilyHidden(false);
          setAdminMode("admin");
          setAdminOpen((current) => (adminTemporarilyHidden ? true : !(current && adminMode === "admin")));
        }}
        onOpenPlatform={() => {
          if (runtimeMe.launcherRole !== "root_admin") return;
          setAdminTemporarilyHidden(false);
          setAdminMode("platform");
          setAdminOpen((current) => (adminTemporarilyHidden ? true : !(current && adminMode === "platform")));
        }}
        onOpenShowcase={() => {
          setAdminTemporarilyHidden(false);
          setAdminOpen(false);
        }}
        onOpenProfileSettings={() => setProfileSettingsOpen(true)}
        onLogout={handleLogout}
        brandLinkUrl={data.settings.brand.logoLinkUrl}
        notifications={hubNotifications}
        onMarkNotificationsRead={markHubNotificationsRead}
        onResolveEngineRoleRequest={async (requestId, action, serviceRole, deliveryId) => {
          const outcome = action === "approve"
            ? await handleApproveEngineWorkflowAccessRequest(requestId, { serviceRole: serviceRole ?? "member" })
            : await handleRejectEngineWorkflowAccessRequest(requestId, {});

          if (!outcome.ok) return;

          if (deliveryId) {
            await markNotificationRead(deliveryId);
          }
          await loadHubNotifications();
        }}
      />

      <main className="launcher-main">
        <ServiceStage
          service={selectedService}
          hasServices={launcherServices.length > 0}
          onLaunch={handleLaunch}
          onSelectPrevious={() => handleStageStep("previous")}
          onSelectNext={() => handleStageStep("next")}
        />
        {adminOpen && me.permissions.canOpenAdmin ? (
          <div
            className={adminTemporarilyHidden ? "launcher-admin-mount launcher-admin-mount--hidden" : "launcher-admin-mount"}
            aria-hidden={adminTemporarilyHidden}
          >
            <Suspense fallback={null}>
            <AdminOverlay
              data={data}
              me={runtimeMe}
              mode={runtimeMe.launcherRole === "root_admin" ? adminMode : "admin"}
              activeClientId={resolvedClientId}
              onClose={() => {
                setAdminTemporarilyHidden(false);
                setAdminOpen(false);
              }}
              onSetUserServiceAccess={handleSetUserServiceAccess}
              onCreateInvite={handleCreateInvite}
              onUpdateInvite={handleUpdateInvite}
              onDeleteInvite={handleDeleteInvite}
              onUpdateAccessRequest={handleUpdateAccessRequest}
              onApproveAccessRequest={handleApproveAccessRequest}
              onRejectAccessRequest={handleRejectAccessRequest}
              onApproveTaskerInviteRequest={handleApproveTaskerInviteRequest}
              onRejectTaskerInviteRequest={handleRejectTaskerInviteRequest}
              onApproveEngineWorkflowAccessRequest={handleApproveEngineWorkflowAccessRequest}
              onRejectEngineWorkflowAccessRequest={handleRejectEngineWorkflowAccessRequest}
              onRetrySync={handleRetrySync}
              onCreateClient={handleCreateClient}
              onUpdateClient={handleUpdateClient}
              onDeleteClient={handleDeleteClient}
              onCreateUser={handleCreateUser}
              onUpdateUser={handleUpdateUser}
              onDeleteUser={handleDeleteUser}
              onUpdateMembership={handleUpdateMembership}
              onDeleteMembership={handleDeleteMembership}
              pendingAccessAssignments={pendingAccessAssignments}
              pendingServiceModuleEntitlements={pendingServiceModuleEntitlements}
              onCreateGroup={handleCreateGroup}
              onUpdateGroup={handleUpdateGroup}
              onDeleteGroup={handleDeleteGroup}
              onUpdateService={handleUpdateService}
              onReorderServices={handleReorderServices}
              onCreateService={handleCreateService}
              onDeleteService={handleDeleteService}
              onUpdateSettings={handleUpdateSettings}
              taskManagerWorkspaces={taskManagerWorkspaces}
              taskManagerWorkspacesLoading={taskManagerWorkspacesLoading}
              taskManagerWorkspacesError={taskManagerWorkspacesError}
              pendingTaskManagerMemberships={pendingTaskManagerMemberships}
              pendingTaskManagerProjectMemberships={pendingTaskManagerProjectMemberships}
              onRefreshTaskManagerWorkspaces={() => void refreshTaskManagerWorkspaces()}
              onSetTaskManagerWorkspaceMemberRole={handleSetTaskManagerWorkspaceMemberRole}
              onSetTaskManagerProjectMemberRole={handleSetTaskManagerProjectMemberRole}
              onSetServiceModuleEntitlement={handleSetServiceModuleEntitlement}
            />
            </Suspense>
          </div>
        ) : null}
        {profileSettingsOpen && activeProfileUser ? (
          <Suspense fallback={null}>
            <ProfileSettingsPanel
              user={activeProfileUser}
              onClose={() => setProfileSettingsOpen(false)}
              onSaveProfile={handleUpdateOwnProfile}
              onChangePassword={handleUpdateOwnPassword}
            />
          </Suspense>
        ) : null}
        <ServiceRail services={launcherServices} selectedServiceId={selectedServiceId} onSelect={handleServiceSelect} />
      </main>
    </div>
  );
}

function syncLauncherServiceLinks(data: Partial<LauncherData>): LauncherData {
  const normalizedData = normalizeLauncherData(data);

  return {
    ...normalizedData,
    services: normalizedData.services.map(syncServiceLaunchLink),
  };
}

function accessAssignmentKey(userId: string, serviceId: string) {
  return `${userId}:${serviceId}`;
}

function AccessRequestScreen({
  onSubmit,
  onLogin,
}: {
  onSubmit: (command: CreateAccessRequestCommand) => Promise<CreateAccessRequestResponse>;
  onLogin: () => void;
}) {
  const [values, setValues] = useState<CreateAccessRequestCommand & { passwordConfirm: string }>({
    email: "",
    firstName: "",
    lastName: "",
    middleName: "",
    phone: "",
    company: "",
    password: "",
    passwordConfirm: "",
  });
  const [status, setStatus] = useState<"idle" | "submitting" | "submitted" | "error">("idle");
  const [message, setMessage] = useState<string | null>(null);
  const isSubmitted = status === "submitted";
  const normalizedEmail = values.email.trim().toLowerCase();
  const passwordMismatch = Boolean(values.passwordConfirm && values.password !== values.passwordConfirm);
  const canSubmit = Boolean(
    normalizedEmail.includes("@") &&
      values.firstName.trim() &&
      values.lastName.trim() &&
      values.middleName.trim() &&
      values.phone.trim() &&
      values.company.trim() &&
      values.password.length >= 8 &&
      values.password === values.passwordConfirm &&
      status !== "submitting"
  );

  const updateField = (field: keyof typeof values, value: string) => {
    setValues((current) => ({ ...current, [field]: value }));
  };

  return (
    <div className="launcher-app nodedc-auth-page">
      <NodeDcAuthBrandHeader />
      <main className="nodedc-auth-page__main">
        <section className="nodedc-auth-card nodedc-access-request-card" aria-live="polite">
          <div className="nodedc-auth-card__copy">
            <h1>NODE.DC.</h1>
            <p>{isSubmitted ? "Вы запросили доступ." : "Работайте во всех измерениях."}</p>
          </div>

          {!isSubmitted ? (
            <p className="nodedc-auth-card__status">
              Заполните обязательные поля и задайте пароль. После подтверждения вы войдёте в NODE.DC по этой почте и паролю.
            </p>
          ) : null}
          {message ? <p className="nodedc-auth-card__status">{message}</p> : null}
          {passwordMismatch ? <p className="nodedc-auth-card__status">Пароли не совпадают.</p> : null}

          {isSubmitted ? (
            <div className="nodedc-auth-card__form">
              <button className="button button--primary" type="button" onClick={onLogin}>
                Войти в NODE.DC
              </button>
            </div>
          ) : (
            <form
              className="nodedc-auth-card__form"
              onSubmit={(event) => {
                event.preventDefault();
                if (!canSubmit) return;

                setStatus("submitting");
                setMessage(null);
                onSubmit({
                  email: normalizedEmail,
                  firstName: values.firstName.trim(),
                  lastName: values.lastName.trim(),
                  middleName: values.middleName.trim(),
                  phone: values.phone.trim(),
                  company: values.company.trim(),
                  password: values.password,
                })
                  .then(() => {
                    setStatus("submitted");
                    setMessage("Заявка отправлена администратору. После подтверждения войдите в NODE.DC по указанному паролю.");
                  })
                  .catch((error) => {
                    setStatus("error");
                    setMessage(error instanceof Error ? error.message : "Не удалось отправить заявку.");
                  });
              }}
            >
              <label className="nodedc-auth-card__field">
                <span>Эл. почта</span>
                <input
                  value={values.email}
                  type="email"
                  placeholder="email@company.ru"
                  autoComplete="email"
                  onChange={(event) => updateField("email", event.target.value)}
                />
              </label>
              <div className="nodedc-auth-card__field-grid">
                <label className="nodedc-auth-card__field">
                  <span>Фамилия</span>
                  <input
                    value={values.lastName}
                    placeholder="Иванов"
                    autoComplete="family-name"
                    onChange={(event) => updateField("lastName", event.target.value)}
                  />
                </label>
                <label className="nodedc-auth-card__field">
                  <span>Имя</span>
                  <input
                    value={values.firstName}
                    placeholder="Иван"
                    autoComplete="given-name"
                    onChange={(event) => updateField("firstName", event.target.value)}
                  />
                </label>
              </div>
              <label className="nodedc-auth-card__field">
                <span>Отчество</span>
                <input value={values.middleName} placeholder="Иванович" onChange={(event) => updateField("middleName", event.target.value)} />
              </label>
              <label className="nodedc-auth-card__field">
                <span>Телефон</span>
                <input
                  value={values.phone}
                  type="tel"
                  placeholder="+7 999 000-00-00"
                  autoComplete="tel"
                  onChange={(event) => updateField("phone", event.target.value)}
                />
              </label>
              <label className="nodedc-auth-card__field">
                <span>Компания</span>
                <input
                  value={values.company}
                  placeholder="Название компании"
                  autoComplete="organization"
                  onChange={(event) => updateField("company", event.target.value)}
                />
              </label>
              <div className="nodedc-auth-card__field-grid">
                <label className="nodedc-auth-card__field">
                  <span>Пароль</span>
                  <input
                    value={values.password}
                    type="password"
                    placeholder="Минимум 8 символов"
                    autoComplete="new-password"
                    onChange={(event) => updateField("password", event.target.value)}
                  />
                </label>
                <label className="nodedc-auth-card__field">
                  <span>Повторите пароль</span>
                  <input
                    value={values.passwordConfirm}
                    type="password"
                    placeholder="Ещё раз"
                    autoComplete="new-password"
                    onChange={(event) => updateField("passwordConfirm", event.target.value)}
                  />
                </label>
              </div>
              <button className="button button--primary" type="submit" disabled={!canSubmit}>
                {status === "submitting" ? "Отправляем заявку" : "Запросить доступ"}
              </button>
              <button className="button button--secondary" type="button" onClick={onLogin}>
                Уже есть аккаунт
              </button>
            </form>
          )}
        </section>
      </main>
    </div>
  );
}

function resolveAuthenticatedContext(
  data: LauncherData,
  session: AuthenticatedSession,
  currentProfileId: string,
  currentClientId: string
): { profileId: string; clientId: string } {
  const sessionEmail = session.user.email?.toLowerCase();
  const sessionSub = session.user.sub;
  const profile =
    data.users.find(
      (user) =>
        (sessionSub && user.authentikUserId === sessionSub) ||
        (sessionEmail && user.email.toLowerCase() === sessionEmail)
    ) ??
    (session.isSuperAdmin ? data.users.find((user) => user.id === "user_root") : undefined) ??
    data.users.find((user) => user.id === currentProfileId) ??
    data.users[0];

  if (!profile) {
    return { profileId: currentProfileId, clientId: currentClientId };
  }

  return {
    profileId: profile.id,
    clientId: resolveDefaultClientId(data, profile.id, currentClientId),
  };
}

function resolveRuntimeProfileId(data: LauncherData, session: AuthSession | null, currentProfileId: string): string {
  if (data.users.some((user) => user.id === currentProfileId)) {
    return currentProfileId;
  }

  if (session?.authenticated) {
    const sessionEmail = session.user.email?.toLowerCase();
    const sessionSub = session.user.sub;
    const sessionUser = data.users.find(
      (user) =>
        (sessionSub && user.authentikUserId === sessionSub) ||
        (sessionEmail && user.email.toLowerCase() === sessionEmail)
    );

    if (sessionUser) {
      return sessionUser.id;
    }
  }

  return data.users[0]?.id ?? currentProfileId;
}

function resolveDefaultClientId(data: LauncherData, userId: string, requestedClientId: string): string {
  const user = data.users.find((item) => item.id === userId);
  const isRoot = user?.id === "user_root";
  const availableClientIds = isRoot
    ? data.clients.map((client) => client.id)
    : data.memberships.filter((membership) => membership.userId === userId && membership.status === "active").map((membership) => membership.clientId);

  if (requestedClientId && availableClientIds.includes(requestedClientId)) {
    return requestedClientId;
  }

  const defaultClientId = profileOptions.find((profile) => profile.userId === userId)?.defaultClientId;

  if (defaultClientId && availableClientIds.includes(defaultClientId)) {
    return defaultClientId;
  }

  return availableClientIds[0] ?? data.clients[0]?.id ?? requestedClientId;
}

function InviteFlowScreen({
  state,
  authenticatedEmail,
  onAccept,
  onRegister,
  onLogin,
  onSwitchAccount,
  onGoHome,
}: {
  state: InviteFlowState;
  authenticatedEmail: string | null;
  onAccept: () => void;
  onRegister: (command: RegisterInviteCommand) => void;
  onLogin: () => void;
  onSwitchAccount: () => void;
  onGoHome: () => void;
}) {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [password, setPassword] = useState("");
  const [passwordConfirm, setPasswordConfirm] = useState("");
  const payload = "payload" in state ? state.payload : undefined;
  const inviteStatus = payload?.invite.status;
  const inviteEmail = payload?.account.email ?? payload?.invite.email ?? "";
  const normalizedInviteEmail = inviteEmail.toLowerCase();
  const existingAccount = Boolean(payload?.account.exists);
  const isAuthenticated = Boolean(authenticatedEmail);
  const isAuthenticatedAsInvitee = Boolean(
    authenticatedEmail &&
      normalizedInviteEmail &&
      authenticatedEmail.toLowerCase() === normalizedInviteEmail
  );
  const isAuthenticatedAsDifferentUser = Boolean(
    authenticatedEmail &&
      normalizedInviteEmail &&
      authenticatedEmail.toLowerCase() !== normalizedInviteEmail
  );
  const isAccepting = state.status === "accepting";
  const isRegistering = state.status === "registering";
  const inviteTargetUrl = payload?.redirectUrl;
  const canOpenInviteTarget = Boolean(
    payload?.invite.source === "tasker_workspace_invite" &&
      inviteTargetUrl &&
      inviteTargetUrl !== "/" &&
      (state.status === "accepted" || inviteStatus === "accepted")
  );
  const requiresAccountSwitch = state.status === "error" && state.message.includes("другую почту");
  const canAccept = Boolean(
    state.status === "ready" &&
      isAuthenticatedAsInvitee &&
      inviteStatus !== "accepted" &&
      inviteStatus !== "expired" &&
      inviteStatus !== "revoked"
  );
  const isTerminalInvite = inviteStatus === "accepted" || inviteStatus === "expired" || inviteStatus === "revoked";
  const canShowRegistrationForm = Boolean(
    payload &&
      !isAuthenticated &&
      !existingAccount &&
      !isTerminalInvite &&
      (state.status === "ready" || state.status === "registering" || state.status === "error")
  );
  const passwordMismatch = Boolean(passwordConfirm && password !== passwordConfirm);
  const normalizedEmail = email.trim();
  const canRegister = Boolean(
    canShowRegistrationForm &&
      state.status !== "registering" &&
      normalizedEmail.includes("@") &&
      name.trim() &&
      password.length >= 8 &&
      password === passwordConfirm
  );
  const details = payload
    ? payload.invite.source === "tasker_workspace_invite"
      ? [
          `Контур: ${payload.client.name}`,
          `Workspace: ${payload.invite.sourceWorkspaceName ?? payload.invite.sourceWorkspaceSlug ?? "Operational Core"}`,
          `Роль: ${membershipRoleLabel(payload.invite.role)}`,
        ]
      : [
          `Рабочая область: ${payload.client.name}`,
          `Роль: ${membershipRoleLabel(payload.invite.role)}`,
        ]
    : ["Проверяем приглашение и платформенную сессию"];
  const statusMessage = resolveInviteStatusMessage(state, {
    existingAccount,
    inviteEmail,
    inviteStatus,
    isAuthenticated,
    isAuthenticatedAsInvitee,
    isAuthenticatedAsDifferentUser,
  });

  return (
    <div className="launcher-app nodedc-auth-page">
      <NodeDcAuthBrandHeader />
      <main className="nodedc-auth-page__main">
        <section className="nodedc-auth-card nodedc-invite-card" aria-live="polite">
          <div className="nodedc-auth-card__copy">
            <h1>Работайте во всех измерениях.</h1>
            <p>Приглашение в NODE.DC.</p>
          </div>

          <div className="nodedc-invite-card__details">
            {details.map((detail) => (
              <span key={detail}>{detail}</span>
            ))}
          </div>

          {statusMessage ? <p className="nodedc-auth-card__status">{statusMessage}</p> : null}
          {state.status === "error" ? <p className="nodedc-auth-card__status">{state.message}</p> : null}
          {passwordMismatch ? <p className="nodedc-auth-card__status">Пароли не совпадают.</p> : null}

          {canShowRegistrationForm ? (
            <form
              className="nodedc-auth-card__form"
              onSubmit={(event) => {
                event.preventDefault();
                if (!canRegister) return;
                onRegister({ email: normalizedEmail, name: name.trim(), password });
              }}
            >
              <label className="nodedc-auth-card__field">
                <span>Эл. почта</span>
                <input
                  value={email}
                  type="email"
                  placeholder="email@company.ru"
                  autoComplete="email"
                  onChange={(event) => setEmail(event.target.value)}
                />
              </label>
              <label className="nodedc-auth-card__field">
                <span>Имя</span>
                <input value={name} placeholder="Ваше имя" autoComplete="name" onChange={(event) => setName(event.target.value)} />
              </label>
              <label className="nodedc-auth-card__field">
                <span>Пароль</span>
                <input
                  value={password}
                  type="password"
                  placeholder="Минимум 8 символов"
                  autoComplete="new-password"
                  onChange={(event) => setPassword(event.target.value)}
                />
              </label>
              <label className="nodedc-auth-card__field">
                <span>Повторите пароль</span>
                <input
                  value={passwordConfirm}
                  type="password"
                  placeholder="Повторите пароль"
                  autoComplete="new-password"
                  onChange={(event) => setPasswordConfirm(event.target.value)}
                />
              </label>
              <button className="button button--primary" type="submit" disabled={!canRegister || isRegistering}>
                {isRegistering ? "Создаём аккаунт" : "Создать аккаунт"}
              </button>
              <button className="button button--secondary" type="button" onClick={onLogin}>
                Уже есть аккаунт
              </button>
            </form>
          ) : existingAccount && !isAuthenticated && !isTerminalInvite ? (
            <button className="button button--primary" type="button" onClick={onLogin}>
              Войти и принять приглашение
            </button>
          ) : (existingAccount && isAuthenticatedAsDifferentUser && !isTerminalInvite) || requiresAccountSwitch ? (
            <button className="button button--primary" type="button" onClick={onSwitchAccount}>
              Сменить аккаунт
            </button>
          ) : state.status === "registered" ? (
            <button className="button button--primary" type="button" onClick={() => redirectToLogin(state.loginUrl)}>
              Войти в NODE.DC
            </button>
          ) : state.status === "error" || state.status === "accepted" || isTerminalInvite ? (
            <button
              className="button button--primary"
              type="button"
              onClick={() => {
                if (canOpenInviteTarget && inviteTargetUrl) {
                  window.location.assign(inviteTargetUrl);
                  return;
                }
                onGoHome();
              }}
            >
              {canOpenInviteTarget ? "Перейти в workspace" : "Перейти в витрину"}
            </button>
          ) : (
            <button className="button button--primary" type="button" disabled={!canAccept || isAccepting} onClick={onAccept}>
              {state.status === "loading" ? "Проверяем" : isAccepting ? "Подключаем доступ" : "Принять приглашение"}
            </button>
          )}
        </section>
      </main>
    </div>
  );
}

function NodeDcAuthBrandHeader() {
  return (
    <header className="nodedc-auth-brand-shell">
      <a href="/" className="nodedc-expanded-brand-link" aria-label="NODE.DC">
        <img src="/nodedc-logo.svg" alt="NODE DC" className="nodedc-expanded-brand-logo" />
      </a>
    </header>
  );
}

function resolveInviteStatusMessage(
  state: InviteFlowState,
  context: {
    existingAccount: boolean;
    inviteEmail: string;
    inviteStatus?: Invite["status"];
    isAuthenticated: boolean;
    isAuthenticatedAsInvitee: boolean;
    isAuthenticatedAsDifferentUser: boolean;
  }
) {
  const {
    existingAccount,
    inviteEmail,
    inviteStatus,
    isAuthenticated,
    isAuthenticatedAsInvitee,
    isAuthenticatedAsDifferentUser,
  } = context;

  if (state.status === "loading") return "Проверяем приглашение.";
  if (state.status === "accepting") return "Подключаем доступ к рабочей области.";
  if (state.status === "registering") return "Создаём аккаунт и подключаем доступ.";
  if (state.status === "registered") return "Аккаунт создан. Теперь войдите в NODE.DC.";
  if (state.status === "accepted" || inviteStatus === "accepted") return "Доступ уже подключён.";
  if (inviteStatus === "expired") return "Срок действия приглашения истёк.";
  if (inviteStatus === "revoked") return "Приглашение отозвано.";
  if (existingAccount && !isAuthenticated) return `Аккаунт ${inviteEmail} уже есть в NODE.DC. Войдите под этой почтой, чтобы принять приглашение.`;
  if (existingAccount && isAuthenticatedAsDifferentUser) return `Сейчас открыт другой аккаунт. Смените пользователя и войдите под ${inviteEmail}.`;
  if (existingAccount && isAuthenticatedAsInvitee) return "Аккаунт найден. Подтвердите подключение к workspace.";
  if (!isAuthenticated) return "Введите почту, имя и пароль для регистрации по приглашению.";
  return null;
}

function AuthStateScreen({
  title,
  description,
  error,
  loginUrl,
}: {
  title: string;
  description: string;
  error?: string | null;
  loginUrl?: string;
}) {
  return (
    <div className="launcher-app">
      <main
        style={{
          display: "grid",
          minHeight: "100vh",
          placeItems: "center",
          padding: "2rem",
        }}
      >
        <section
          style={{
            display: "grid",
            width: "min(34rem, 100%)",
            gap: "1rem",
            padding: "2rem",
            borderRadius: "1.75rem",
            background: "rgba(255, 255, 255, 0.08)",
            textAlign: "center",
          }}
        >
          <img src="/nodedc-logo.svg" alt="NODE.DC" style={{ justifySelf: "center", width: "11rem" }} />
          <h1 style={{ margin: 0 }}>{title}</h1>
          <p style={{ margin: 0, color: "var(--text-secondary)", lineHeight: 1.5 }}>{description}</p>
          {error ? <p style={{ margin: 0, color: "var(--warning)", lineHeight: 1.45 }}>{error}</p> : null}
          {loginUrl ? (
            <button className="button button--primary" type="button" onClick={() => redirectToLogin(loginUrl)}>
              Войти
            </button>
          ) : null}
        </section>
      </main>
    </div>
  );
}

function parseInviteToken(pathname: string) {
  const match = /^\/invite\/([^/?#]+)\/?$/.exec(pathname);
  return match?.[1] ? decodeURIComponent(match[1]) : null;
}

function mapNotificationDeliveryToLauncherItem(delivery: NotificationDelivery): LauncherNotificationItem {
  const requestId = notificationString(delivery.entityId)
    ?? notificationString(delivery.meta?.requestId)
    ?? notificationString(delivery.eventPayload?.requestId);
  const requestedServiceRole = notificationEngineServiceRole(
    delivery.meta?.requestedServiceRole ?? delivery.eventPayload?.requestedServiceRole ?? delivery.eventPayload?.serviceRole
  );

  return {
    id: delivery.id,
    kind: notificationKindFromDelivery(delivery),
    title: delivery.title,
    description: delivery.body,
    meta: notificationString(delivery.meta?.meta),
    displayStatus: notificationString(delivery.meta?.displayStatus),
    status: delivery.status,
    createdAt: delivery.createdAt,
    updatedAt: delivery.updatedAt,
    actionKind: delivery.actionType === "engine.role_upgrade.review" ? "engine-role-request" : undefined,
    requestId: delivery.actionType === "engine.role_upgrade.review" ? requestId : undefined,
    requestedServiceRole,
  };
}

function notificationKindFromDelivery(delivery: NotificationDelivery): LauncherNotificationItem["kind"] {
  const source = String(delivery.sourceService || "").toLowerCase();
  const eventType = String(delivery.eventType || "").toLowerCase();
  const entityType = String(delivery.entityType || "").toLowerCase();

  if (source === "tasker" || source === "operational-core" || eventType.startsWith("tasker.") || entityType.startsWith("tasker")) {
    return "operational-core";
  }

  if (source === "engine" || eventType.startsWith("engine.") || entityType.startsWith("engine_")) {
    return "engine";
  }

  return "nodedc";
}

function notificationString(value: unknown): string | undefined {
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
}

function notificationEngineServiceRole(value: unknown): "viewer" | "member" | null {
  return value === "viewer" || value === "member" ? value : null;
}

function isAccessRequestPath(pathname: string) {
  return /^\/(?:request-access|access-request)\/?$/.test(pathname);
}

function membershipRoleLabel(role: ClientMembership["role"]) {
  return {
    client_owner: "Владелец клиента",
    client_admin: "Администратор клиента",
    member: "Участник",
  }[role];
}

function buildLoginRedirectUrl(loginUrl?: string, options: { returnTo?: string | null } = {}) {
  const url = new URL(loginUrl || "/auth/login", window.location.origin);

  if (options.returnTo === null) {
    url.searchParams.delete("returnTo");
  } else if (!url.searchParams.has("returnTo")) {
    const returnTo = options.returnTo ?? `${window.location.pathname}${window.location.search}${window.location.hash}`;

    if (returnTo && returnTo !== "/") {
      url.searchParams.set("returnTo", returnTo);
    } else if (options.returnTo === "/") {
      url.searchParams.set("returnTo", "/");
    }
  }

  return url.origin === window.location.origin ? `${url.pathname}${url.search}${url.hash}` : url.toString();
}

function redirectToLogin(loginUrl?: string, options?: { returnTo?: string | null }) {
  const redirectUrl = buildLoginRedirectUrl(loginUrl, options);
  const now = Date.now();

  if (lastAuthRedirect && now - lastAuthRedirect.startedAt < 1500) {
    return;
  }

  lastAuthRedirect = { url: redirectUrl, startedAt: now };
  window.location.replace(redirectUrl);
}
