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;
  visibility?: string;
  config?: Record<string, unknown>;
};

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 = {
  includeInternal?: boolean;
  tenantId?: string;
};

type OrgChartLoginOptions = {
  includeInternal?: boolean;
  returnTo?: string;
};

export const GPDTDC_GRADE_OPTIONS = [
  "연구원",
  "선임",
  "책임",
  "수석",
  "부사장",
  "사장",
] as const;

export const HANMAC_FAMILY_GRADE_OPTIONS = [
  "사원",
  "대리",
  "과장",
  "차장",
  "부장",
  "이사",
  "상무",
  "전무",
  "부사장",
  "사장",
  "회장",
] as const;

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 resolveTenantTarget<T extends TenantFilterTarget>(
  target: TenantFilterTarget | undefined,
  tenants: T[],
) {
  if (!target) return undefined;
  const tenantID = target.id ?? target.tenantId ?? "";
  const tenantSlug = target.slug ?? target.tenantSlug ?? "";
  return (
    tenants.find((tenant) => tenantID && tenant.id === tenantID) ??
    tenants.find(
      (tenant) =>
        tenantSlug &&
        tenant.slug?.trim().toLowerCase() === tenantSlug.trim().toLowerCase(),
    ) ??
    target
  );
}

function isGPDTDCTenant<T extends TenantFilterTarget>(
  target: TenantFilterTarget | undefined,
  tenants: T[],
) {
  const tenant = resolveTenantTarget(target, tenants);
  if (!tenant) return false;

  const tenantById = new Map(
    tenants
      .filter((item) => item.id?.trim())
      .map((item) => [item.id as string, item]),
  );
  let current: TenantFilterTarget | undefined = tenant;
  const visited = new Set<string>();

  while (current) {
    const slug = current.slug?.trim().toLowerCase();
    if (slug === "gpdtdc") {
      return true;
    }
    const parentId = current.parentId ?? "";
    if (!parentId || visited.has(parentId)) {
      return false;
    }
    visited.add(parentId);
    current = tenantById.get(parentId);
  }

  return false;
}

export function getTenantGradeOptions<T extends TenantFilterTarget>(
  target: TenantFilterTarget | undefined,
  tenants: T[],
) {
  return isGPDTDCTenant(target, tenants)
    ? [...GPDTDC_GRADE_OPTIONS]
    : [...HANMAC_FAMILY_GRADE_OPTIONS];
}

function isPublicRepresentativeTenant(tenant: TenantFilterTarget) {
  const visibility = String(
    tenant.visibility ?? tenant.config?.visibility ?? "public",
  )
    .trim()
    .toLowerCase();
  return visibility !== "internal" && visibility !== "private";
}

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?.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) &&
      isPublicRepresentativeTenant(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);
  }
  if (options.includeInternal) {
    params.set("includeInternal", "true");
  }

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

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

export function buildAuthenticatedOrgChartUrl(
  baseUrl?: string,
  options: OrgChartLoginOptions = { includeInternal: true },
) {
  const normalizedBase = (baseUrl ?? "").replace(/\/+$/, "");
  let returnTo = options.returnTo?.trim() || "/chart";
  if (options.includeInternal && returnTo.startsWith("/chart")) {
    const [path, query = ""] = returnTo.split("?", 2);
    const params = new URLSearchParams(query);
    params.set("includeInternal", "true");
    returnTo = `${path}?${params.toString()}`;
  }
  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,
  };
}
