import type { MatrixAuthorityContext } from '@open-matrix/core';

type HostLike = {
  getAttribute?: (name: string) => string | null;
  dataset?: Record<string, string | undefined>;
} | null | undefined;

type HostLocatorLike = {
  closest?: (selector: string) => unknown;
  getRootNode?: () => unknown;
} | null | undefined;

export const HOSTED_RUNTIME_TARGET_CHANGED_EVENT = 'matrix:hosted-runtime-target-changed';

export interface IHostedRuntimeTargetInfo {
  runtimeRoot: string;
  runtimeInstanceRoot: string;
  authorityRoot: string;
  assetHostRoot: string;
  platformServiceRoot: string;
  sessionAuthorityRoot: string;
  selectedAuthorityRoot: string;
  platformRoot: string;
  targetSource: string;
  targetKind: string;
  requestedRoot: string;
  unavailableReason: string;
  isAuthenticated: boolean;
  isPlatformAdmin: boolean;
  selectableAuthorityRoots: readonly string[];
}

function trimValue(value: string | null | undefined): string {
  return typeof value === 'string' ? value.trim() : '';
}

function readBoolean(value: string | null | undefined): boolean {
  return value === 'true';
}

function readCsv(value: string | null | undefined): readonly string[] {
  if (typeof value !== 'string') {
    return [];
  }
  return value.split(',').map((item) => item.trim()).filter((item) => item.length > 0);
}

export function resolveHostedRuntimeTargetInfo(host: HostLike): IHostedRuntimeTargetInfo {
  const runtimeInstanceRoot = trimValue(
    host?.dataset?.runtimeInstanceRoot
    ?? host?.getAttribute?.('runtime-root')
    ?? host?.dataset?.runtimeRoot
    ?? host?.dataset?.runtimeTargetRoot,
  );
  const selectedAuthorityRoot = trimValue(host?.dataset?.selectedAuthorityRoot);
  const sessionAuthorityRoot = trimValue(host?.dataset?.sessionAuthorityRoot);
  return {
    runtimeRoot: runtimeInstanceRoot,
    runtimeInstanceRoot,
    authorityRoot: selectedAuthorityRoot || sessionAuthorityRoot,
    assetHostRoot: trimValue(host?.dataset?.assetHostRoot),
    platformServiceRoot: trimValue(host?.dataset?.platformServiceRoot),
    sessionAuthorityRoot,
    selectedAuthorityRoot,
    platformRoot: trimValue(
      host?.dataset?.platformServiceRoot
      ?? host?.getAttribute?.('bus-root')
      ?? host?.dataset?.platformRoot,
    ),
    targetSource: trimValue(host?.dataset?.runtimeTargetSource),
    targetKind: trimValue(host?.dataset?.runtimeTargetKind),
    requestedRoot: trimValue(host?.dataset?.runtimeTargetRequestedRoot),
    unavailableReason: trimValue(host?.dataset?.runtimeTargetUnavailableReason),
    isAuthenticated: readBoolean(host?.dataset?.authenticated),
    isPlatformAdmin: readBoolean(host?.dataset?.isPlatformAdmin),
    selectableAuthorityRoots: readCsv(host?.dataset?.selectableAuthorityRoots),
  };
}

export function hostedTargetAuthorityContext(info: IHostedRuntimeTargetInfo): MatrixAuthorityContext {
  return {
    assetHostRoot: info.assetHostRoot,
    platformServiceRoot: info.platformServiceRoot || info.platformRoot,
    sessionAuthorityRoot: info.sessionAuthorityRoot || null,
    selectedAuthorityRoot: info.selectedAuthorityRoot || null,
    runtimeInstanceRoot: info.runtimeInstanceRoot || info.runtimeRoot || null,
    isAuthenticated: info.isAuthenticated,
    isPlatformAdmin: info.isPlatformAdmin,
    selectableAuthorityRoots: info.selectableAuthorityRoots,
  };
}

export function resolveHostedRuntimeTargetHost(node: HostLocatorLike): HostLike {
  let current = node;
  const visited = new Set<unknown>();

  while (current && !visited.has(current)) {
    visited.add(current);
    const direct = typeof current.closest === 'function'
      ? current.closest('matrix-dsl-host')
      : null;
    if (direct && typeof direct === 'object') {
      return direct as HostLike;
    }
    const root = typeof current.getRootNode === 'function'
      ? current.getRootNode()
      : null;
    if (!root || typeof root !== 'object' || !('host' in root)) {
      break;
    }
    current = (root as { host?: HostLocatorLike }).host ?? null;
  }

  return null;
}

export function hostedRuntimeTargetInfoEquals(
  left: IHostedRuntimeTargetInfo,
  right: IHostedRuntimeTargetInfo,
): boolean {
  return (
    left.runtimeRoot === right.runtimeRoot
    && left.runtimeInstanceRoot === right.runtimeInstanceRoot
    && left.authorityRoot === right.authorityRoot
    && left.assetHostRoot === right.assetHostRoot
    && left.platformServiceRoot === right.platformServiceRoot
    && left.sessionAuthorityRoot === right.sessionAuthorityRoot
    && left.selectedAuthorityRoot === right.selectedAuthorityRoot
    && left.platformRoot === right.platformRoot
    && left.targetSource === right.targetSource
    && left.targetKind === right.targetKind
    && left.requestedRoot === right.requestedRoot
    && left.unavailableReason === right.unavailableReason
    && left.isAuthenticated === right.isAuthenticated
    && left.isPlatformAdmin === right.isPlatformAdmin
    && left.selectableAuthorityRoots.length === right.selectableAuthorityRoots.length
    && left.selectableAuthorityRoots.every((value, index) => value === right.selectableAuthorityRoots[index])
  );
}
