export type OrgChartTenantSelection = {
  id: string;
  name: string;
};

export type TenantFilterTarget = {
  id?: string;
  tenantId?: string;
  slug?: string;
  tenantSlug?: string;
  type?: string;
  parentId?: string | null;
  name?: string;
  tenantName?: string;
};

export type HanmacFamilyUserTarget = {
  companyCode?: string;
  tenantSlug?: string;
  tenant?: TenantFilterTarget;
  joinedTenants?: TenantFilterTarget[];
  metadata?: Record<string, unknown>;
};

type OrgChartPickerMessage = {
  type?: unknown;
  payload?: {
    selections?: Array<{
      type?: unknown;
      id?: unknown;
      name?: unknown;
    }>;
  };
};

type OrgChartTenantPickerOptions = {
  tenantId?: string;
};

type OrgChartLoginOptions = {
  returnTo?: string;
};

function isSystemTenant(tenant: TenantFilterTarget) {
  const slug = tenant.slug?.trim().toLowerCase();
  const type = tenant.type?.trim().toUpperCase();

  return (
    !tenant.id?.trim() ||
    !tenant.slug?.trim() ||
    type === "SYSTEM" ||
    slug === "system" ||
    slug === "global"
  );
}

function isInTenantSubtree<T extends TenantFilterTarget>(
  tenant: T,
  rootTenantId: string,
  tenantById: Map<string, T>,
) {
  if (!rootTenantId) {
    return false;
  }
  if (tenant.id === rootTenantId) {
    return true;
  }

  const visitedTenantIds = new Set<string>();
  let parentId = tenant.parentId ?? "";
  while (parentId) {
    if (parentId === rootTenantId) {
      return true;
    }
    if (visitedTenantIds.has(parentId)) {
      return false;
    }
    visitedTenantIds.add(parentId);
    parentId = tenantById.get(parentId)?.parentId ?? "";
  }

  return false;
}

function resolveHanmacFamilyTenantId<T extends TenantFilterTarget>(
  tenants: T[],
  hanmacFamilyTenantId?: string,
) {
  const envTenantId = hanmacFamilyTenantId?.trim();
  if (envTenantId) return envTenantId;

  return (
    tenants.find((tenant) => tenant.slug?.toLowerCase() === "hanmac-family")
      ?.id ?? ""
  );
}

export function isHanmacFamilyTenant<T extends TenantFilterTarget>(
  tenant: T | undefined,
  tenants: T[],
  hanmacFamilyTenantId?: string,
) {
  if (!tenant || !tenant.id) return false;

  const rootTenantId = resolveHanmacFamilyTenantId(
    tenants,
    hanmacFamilyTenantId,
  );
  if (!rootTenantId) return false;

  const tenantById = new Map(
    tenants
      .filter((item) => item.id?.trim())
      .map((item) => [item.id as string, item]),
  );
  const target = tenantById.get(tenant.id) ?? tenant;

  return isInTenantSubtree(target, rootTenantId, tenantById);
}

export function isHanmacFamilyUser<T extends TenantFilterTarget>(
  user: HanmacFamilyUserTarget,
  tenants: T[],
  hanmacFamilyTenantId?: string,
) {
  const metadataAppointments = Array.isArray(
    user.metadata?.additionalAppointments,
  )
    ? user.metadata.additionalAppointments
        .map((appointment) => appointment as TenantFilterTarget)
        .filter(
          (appointment) =>
            typeof appointment.tenantId === "string" ||
            typeof appointment.id === "string" ||
            typeof appointment.tenantSlug === "string" ||
            typeof appointment.slug === "string",
        )
        .map((appointment) => ({
          id: appointment.id ?? appointment.tenantId,
          slug: appointment.slug ?? appointment.tenantSlug,
          parentId: appointment.parentId,
          type: appointment.type,
          name: appointment.name ?? appointment.tenantName,
        }))
    : [];
  const tenantBySlug = new Map(
    tenants
      .filter((tenant) => tenant.slug?.trim())
      .map((tenant) => [tenant.slug?.toLowerCase() as string, tenant]),
  );
  const tenantById = new Map(
    tenants
      .filter((tenant) => tenant.id?.trim())
      .map((tenant) => [tenant.id as string, tenant]),
  );
  const tenantCandidates = [
    user.tenant,
    ...(user.joinedTenants ?? []),
    ...metadataAppointments,
    ...metadataAppointments.map((appointment) =>
      tenantById.get(appointment.id ?? ""),
    ),
    tenantBySlug.get(user.tenantSlug?.toLowerCase() ?? ""),
  ];

  return tenantCandidates.some((tenant) =>
    isHanmacFamilyTenant(tenant, tenants, hanmacFamilyTenantId),
  );
}

export function filterNonHanmacFamilyTenants<T extends TenantFilterTarget>(
  tenants: T[],
  hanmacFamilyTenantId?: string,
) {
  const rootTenantId = resolveHanmacFamilyTenantId(
    tenants,
    hanmacFamilyTenantId,
  );
  const tenantById = new Map(
    tenants
      .filter((tenant) => tenant.id?.trim())
      .map((tenant) => [tenant.id as string, tenant]),
  );

  return tenants.filter(
    (tenant) =>
      !isSystemTenant(tenant) &&
      !isInTenantSubtree(tenant, rootTenantId, tenantById),
  );
}

export function buildOrgChartTenantPickerUrl(
  baseUrl?: string,
  options: OrgChartTenantPickerOptions = {},
) {
  const normalizedBase = (baseUrl ?? "").replace(/\/+$/, "");
  const params = new URLSearchParams({
    mode: "single",
    select: "tenant",
    width: "400",
    height: "600",
  });
  const tenantId = options.tenantId?.trim();
  if (tenantId) {
    params.set("tenantId", tenantId);
  }

  return `${normalizedBase}/embed/picker?${params.toString()}`;
}

export function buildAuthenticatedOrgChartTenantPickerUrl(
  baseUrl?: string,
  options: OrgChartTenantPickerOptions = {},
) {
  const pickerUrl = buildOrgChartTenantPickerUrl("", options);
  return buildAuthenticatedOrgChartUrl(baseUrl, { returnTo: pickerUrl });
}

export function buildAuthenticatedOrgChartUrl(
  baseUrl?: string,
  options: OrgChartLoginOptions = {},
) {
  const normalizedBase = (baseUrl ?? "").replace(/\/+$/, "");
  const returnTo = options.returnTo?.trim() || "/chart";
  const params = new URLSearchParams({
    auto: "1",
    returnTo,
  });

  return `${normalizedBase}/login?${params.toString()}`;
}

export function parseOrgChartTenantSelection(
  message: unknown,
): OrgChartTenantSelection | null {
  const data = message as OrgChartPickerMessage;
  if (data?.type !== "orgfront:picker:confirm") {
    return null;
  }

  const selection = data.payload?.selections?.[0];
  if (
    selection?.type !== "tenant" ||
    typeof selection.id !== "string" ||
    typeof selection.name !== "string" ||
    selection.id.trim() === ""
  ) {
    return null;
  }

  return {
    id: selection.id,
    name: selection.name,
  };
}
