import { createHash } from 'node:crypto';

export type DocsVersionEntry = {
  version: string;
  path: string;
};

export type DocsChannelEntry = {
  label: string;
  path: string;
};

export type DocsVersionManifest = {
  latestStable: string;
  channels: DocsChannelEntry[];
  versions: DocsVersionEntry[];
};

const STABLE_TAG_PATTERN = /^v\d+\.\d+\.\d+$/;

export function isStableReleaseTag(tag: string): boolean {
  return STABLE_TAG_PATTERN.test(tag);
}

function parseStableVersion(tag: string): [number, number, number] {
  const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(tag);
  if (!match) {
    throw new Error(`Invalid stable SubMiner version tag: ${tag}`);
  }

  return [Number(match[1]), Number(match[2]), Number(match[3])];
}

export function compareStableVersionsDesc(a: string, b: string): number {
  if (!isStableReleaseTag(a) && !isStableReleaseTag(b)) return a.localeCompare(b);
  if (!isStableReleaseTag(a)) return 1;
  if (!isStableReleaseTag(b)) return -1;

  const parsedA = parseStableVersion(a);
  const parsedB = parseStableVersion(b);

  for (let index = 0; index < parsedA.length; index += 1) {
    const difference = parsedB[index]! - parsedA[index]!;
    if (difference !== 0) return difference;
  }

  return 0;
}

export function versionPath(version: string): string {
  return `/v/${version.replace(/^v/, '')}/`;
}

export function versionOutputPath(version: string): string {
  return `v/${version.replace(/^v/, '')}`;
}

export function versionArchiveCacheName(version: string, sharedInternalsHash: string): string {
  return `${sharedInternalsHash.slice(0, 12)}-${version}`;
}

export function versionArchiveCacheKey(options: {
  sharedInternalsHash: string;
  manifestJson: string;
}): string {
  const hash = createHash('sha256');
  hash.update('shared-internals:');
  hash.update(options.sharedInternalsHash);
  hash.update('\nmanifest:');
  hash.update(options.manifestJson);
  return hash.digest('hex');
}

export function stableTagsWithDocs(
  tags: string[],
  hasDocsSite: (tag: string) => boolean,
): string[] {
  return tags.filter(isStableReleaseTag).filter(hasDocsSite).sort(compareStableVersionsDesc);
}

export function buildVersionManifest(options: {
  latestStable: string;
  stableVersions: string[];
}): DocsVersionManifest {
  return {
    latestStable: options.latestStable,
    channels: [
      { label: 'Latest stable', path: '/' },
      { label: 'main', path: '/main/' },
    ],
    versions: options.stableVersions.map((version) => ({
      version,
      path: versionPath(version),
    })),
  };
}
