import type { TenantSummary, UserSummary } from "../../lib/adminApi";
import { buildTenantFullTree, type TenantNode } from "../../lib/tenantTree";
import { orderHanmacFamilyChildren } from "./hanmacFamilyOrder";
import type { OrgPickerTreeNode } from "./pickerTypes";
import { filterTenantsByVisibility } from "./tenantVisibility";
import { getOrgChartUserDisplayName } from "./userDisplay";

function getUserTenantSlug(user: UserSummary) {
  return user.tenantSlug?.toLowerCase() || "";
}

function isOrgFrontTenantType(tenant: TenantSummary) {
  return ["COMPANY_GROUP", "COMPANY", "ORGANIZATION", "USER_GROUP"].includes(
    tenant.type.toUpperCase(),
  );
}

function getCompanyGroupId(node: TenantNode, allTenants: TenantSummary[]) {
  let cursor: TenantSummary | undefined = node;
  const byId = new Map(allTenants.map((tenant) => [tenant.id, tenant]));

  while (cursor?.parentId) {
    const parent = byId.get(cursor.parentId);
    if (!parent) break;
    cursor = parent;
  }

  return cursor?.type === "COMPANY_GROUP" ? cursor.id : node.id;
}

function isHanmacFamilyCompanyGroup(tenant: TenantSummary) {
  return (
    tenant.type.toUpperCase() === "COMPANY_GROUP" &&
    tenant.slug.toLowerCase() === "hanmac-family"
  );
}

function findTenantByRef(tenants: TenantSummary[], ref?: string) {
  const normalizedRef = ref?.trim().toLowerCase();
  if (!normalizedRef) return undefined;

  return (
    tenants.find((tenant) => tenant.slug.toLowerCase() === normalizedRef) ??
    tenants.find((tenant) => tenant.id === ref)
  );
}

function tenantToPickerNode(
  tenant: TenantNode,
  usersBySlug: Map<string, UserSummary[]>,
): OrgPickerTreeNode {
  const tenantChildren = orderHanmacFamilyChildren(tenant, tenant.children).map(
    (child) => tenantToPickerNode(child, usersBySlug),
  );
  const userChildren = (usersBySlug.get(tenant.slug.toLowerCase()) || []).map(
    (user) => ({
      type: "user" as const,
      id: user.id,
      name: getOrgChartUserDisplayName(user, tenant),
      parentId: tenant.id,
      user,
      children: [],
    }),
  );

  return {
    type: "tenant",
    id: tenant.id,
    name: tenant.name,
    parentId: tenant.parentId ?? null,
    tenant,
    children: [...userChildren, ...tenantChildren],
  };
}

function findTenantNode(
  roots: TenantNode[],
  tenantRef: string,
): TenantNode | undefined {
  const findBySlug = (node: TenantNode): TenantNode | undefined => {
    if (node.slug.toLowerCase() === tenantRef.trim().toLowerCase()) {
      return node;
    }
    for (const child of node.children) {
      const match = findBySlug(child);
      if (match) return match;
    }
    return undefined;
  };
  const findById = (node: TenantNode): TenantNode | undefined => {
    if (node.id === tenantRef) return node;
    for (const child of node.children) {
      const match = findById(child);
      if (match) return match;
    }
    return undefined;
  };

  for (const root of roots) {
    const slugMatch = findBySlug(root);
    if (slugMatch) return slugMatch;
  }
  for (const root of roots) {
    const idMatch = findById(root);
    if (idMatch) return idMatch;
  }
  return undefined;
}

export function buildOrgPickerTree({
  includeInternal = false,
  tenants,
  users,
  rootTenantId,
  tenantId,
}: {
  includeInternal?: boolean;
  tenants: TenantSummary[];
  users: UserSummary[];
  rootTenantId?: string;
  tenantId?: string;
}) {
  const visibleTenants = filterTenantsByVisibility(
    tenants.filter(isOrgFrontTenantType),
    includeInternal ? "internal" : "public",
  );
  const usersBySlug = new Map<string, UserSummary[]>();
  for (const user of users) {
    if (user.status !== "active") continue;
    const slug = getUserTenantSlug(user);
    if (!slug) continue;
    const list = usersBySlug.get(slug) || [];
    list.push(user);
    usersBySlug.set(slug, list);
  }

  const companyGroup =
    findTenantByRef(visibleTenants, rootTenantId) ??
    visibleTenants.find(isHanmacFamilyCompanyGroup) ??
    visibleTenants.find((tenant) => tenant.type === "COMPANY_GROUP") ??
    visibleTenants.find((tenant) => !tenant.parentId);

  if (!companyGroup) return { roots: [], companies: [], companyGroupId: "" };

  const { currentBase } = buildTenantFullTree(visibleTenants, companyGroup.id);
  const groupNode =
    currentBase ??
    buildTenantFullTree(visibleTenants).subTree.find(
      (node) => node.id === companyGroup.id,
    );

  if (!groupNode) return { roots: [], companies: [], companyGroupId: "" };

  const companies = orderHanmacFamilyChildren(
    groupNode,
    groupNode.children,
  ).filter((node) => node.type === "COMPANY");
  const scopedRoot = tenantId
    ? findTenantNode([groupNode], tenantId)
    : groupNode;
  const filteredRoots = scopedRoot ? [scopedRoot] : [];
  const roots = filteredRoots.map((node) =>
    tenantToPickerNode(node, usersBySlug),
  );

  return {
    roots,
    companies: companies.map((company) => ({
      id: company.id,
      name: company.name,
      companyGroupTenantId: getCompanyGroupId(company, tenants),
    })),
    companyGroupId: companyGroup.id,
  };
}

export function flattenDescendants(node: OrgPickerTreeNode) {
  const descendants: OrgPickerTreeNode[] = [];
  const walk = (current: OrgPickerTreeNode) => {
    for (const child of current.children) {
      descendants.push(child);
      walk(child);
    }
  };

  walk(node);
  return descendants;
}
