/**
 * Model Space / Paper Space (Layout) discovery for CadParseResult.
 *
 * DWG entities are owned by a block header handle. System blocks
 * *Model_Space / *Paper_Space (and *Paper_Space0…) are the two spaces
 * AutoCAD switches between. Viewer2D must render only one at a time —
 * drawing both stacks survey-model geometry on top of layout sheet
 * geometry and produces the "all text piled in the center" glitch.
 */

export type CadSpaceKind = 'model' | 'paper' | 'other';

export type CadSpace = {
  /** Stable id for UI (hex handle string, same as ownerHandle.value.toString(16)). */
  id: string;
  /** Display label, e.g. "Model" / "Layout" / raw block name. */
  label: string;
  /** Raw block name from tables.blocks. */
  name: string;
  /** Block handle as number when available. */
  handle: number | null;
  /** Hex string matching entity ownerHandle.value.toString(16). */
  handleHex: string;
  kind: CadSpaceKind;
  entityCount: number;
};

function handleToHex(handle: unknown): string | null {
  if (handle == null) return null;
  if (typeof handle === 'object' && handle !== null && 'value' in handle) {
    const v = (handle as { value?: unknown }).value;
    if (typeof v === 'number' && Number.isFinite(v)) return v.toString(16);
    if (typeof v === 'string' && v.length) {
      const n = Number(v);
      return Number.isFinite(n) ? n.toString(16) : v.toLowerCase();
    }
    return null;
  }
  if (typeof handle === 'number' && Number.isFinite(handle)) return handle.toString(16);
  if (typeof handle === 'string' && handle.length) {
    const n = Number(handle);
    return Number.isFinite(n) ? n.toString(16) : handle.toLowerCase();
  }
  return null;
}

function ownerHex(entity: { ownerHandle?: unknown }): string | null {
  return handleToHex(entity.ownerHandle);
}

function classifySpaceName(raw: string): CadSpaceKind | null {
  const name = raw.toLowerCase().replace(/\*/g, '').trim();
  if (!name) return null;
  if (name === 'model_space' || name === 'ms' || name.startsWith('model')) return 'model';
  if (name === 'paper_space' || name === 'ps' || name.startsWith('paper')) return 'paper';
  return null;
}

function spaceLabel(kind: CadSpaceKind, name: string, paperIndex: number): string {
  if (kind === 'model') return 'Model';
  if (kind === 'paper') {
    // First paper space → "Layout"; further ones keep a disambiguator.
    if (paperIndex <= 1) return 'Layout';
    return `Layout (${name.replace(/^\*/, '')})`;
  }
  return name;
}

/**
 * List model/paper space blocks that own at least zero entities.
 * Always includes Model/Paper headers found in tables.blocks.
 */
export function listCadSpaces(result: {
  entities?: unknown[];
  tables?: { blocks?: Array<{ name?: string; handle?: unknown }> };
} | null | undefined): CadSpace[] {
  const blocks = result?.tables?.blocks ?? [];
  const entities = (result?.entities ?? []) as Array<{ ownerHandle?: unknown }>;

  const counts = new Map<string, number>();
  for (const e of entities) {
    const hex = ownerHex(e);
    if (!hex) continue;
    counts.set(hex, (counts.get(hex) || 0) + 1);
  }

  const spaces: CadSpace[] = [];
  let paperIndex = 0;
  for (const b of blocks) {
    const kind = classifySpaceName(b.name ?? '');
    if (!kind) continue;
    const hex = handleToHex(b.handle);
    if (!hex) continue;
    if (kind === 'paper') paperIndex += 1;
    const handleNum =
      typeof b.handle === 'number'
        ? b.handle
        : typeof b.handle === 'object' && b.handle && 'value' in b.handle
          ? Number((b.handle as { value: unknown }).value)
          : null;
    spaces.push({
      id: hex,
      label: spaceLabel(kind, b.name ?? '', paperIndex),
      name: b.name ?? '',
      handle: Number.isFinite(handleNum as number) ? (handleNum as number) : null,
      handleHex: hex,
      kind,
      entityCount: counts.get(hex) || 0,
    });
  }

  // Stable order: Model first, then papers in table order.
  spaces.sort((a, b) => {
    if (a.kind !== b.kind) {
      if (a.kind === 'model') return -1;
      if (b.kind === 'model') return 1;
    }
    return 0;
  });
  return spaces;
}

/**
 * Default tab when a file is opened:
 * - Paper/Layout with entities if any (civil sheet drawings often leave Layout active)
 * - else Model
 * - else first listed space
 */
export function pickDefaultSpace(spaces: CadSpace[]): CadSpace | null {
  if (!spaces.length) return null;
  const paperWithContent = spaces.find((s) => s.kind === 'paper' && s.entityCount > 0);
  if (paperWithContent) return paperWithContent;
  const model = spaces.find((s) => s.kind === 'model');
  if (model) return model;
  return spaces[0] ?? null;
}

/**
 * Owners allowed when filtering to one space: the space handle itself plus
 * handles of entities that live under it (e.g. INSERT → ATTRIB children).
 */
export function buildAllowedOwnerHexes(
  entities: Array<{ handle?: unknown; ownerHandle?: unknown }>,
  activeSpaceHex: string,
  rounds = 3,
): Set<string> {
  const allowed = new Set<string>([activeSpaceHex]);
  for (let pass = 0; pass < rounds; pass++) {
    let grew = false;
    for (const ent of entities) {
      const oh = ownerHex(ent);
      if (!oh || !allowed.has(oh)) continue;
      const h = handleToHex(ent.handle);
      if (h && !allowed.has(h)) {
        allowed.add(h);
        grew = true;
      }
    }
    if (!grew) break;
  }
  return allowed;
}

export function isSystemSpaceBlockName(name: string): boolean {
  return classifySpaceName(name) != null;
}
