import upath from 'upath';
import { logger } from '../../../logger';
import { DockerDatasource } from '../../datasource/docker';
import type { PackageDependency } from '../types';
import { removeOCIPrefix } from './oci';
import type { ChartDefinition, Repository } from './types';

export function parseRepository(
  depName: string,
  repositoryURL: string,
): PackageDependency {
  const res: PackageDependency = {};

  try {
    const url = new URL(repositoryURL);
    switch (url.protocol) {
      case 'oci:':
        res.datasource = DockerDatasource.id;
        res.packageName = `${removeOCIPrefix(repositoryURL)}/${depName}`;
        // https://github.com/helm/helm/issues/10312
        // https://github.com/helm/helm/issues/10678
        res.pinDigests = false;
        break;
      case 'file:':
        res.skipReason = 'local-dependency';
        break;
      default:
        res.registryUrls = [repositoryURL];
    }
  } catch (err) {
    logger.debug({ err }, 'Error parsing url');
    res.skipReason = 'invalid-url';
  }
  return res;
}

/**
 * Resolves alias in repository string.
 *
 * @param repository to be resolved string
 * @param registryAliases Records containing registryAliases as key and to be resolved URLs as values
 *
 * @returns  resolved alias. If repository does not contain an alias the repository string will be returned. Should it contain an alias which can not be resolved using `registryAliases`, null will be returned
 */
export function resolveAlias(
  repository: string,
  registryAliases: Record<string, string>,
): string | null {
  if (!isAlias(repository)) {
    return repository;
  }

  const repoWithPrefixRemoved = repository.slice(
    repository.startsWith('@') ? 1 : 6,
  );
  const alias = registryAliases[repoWithPrefixRemoved];
  if (alias) {
    return alias;
  }
  return null;
}

export function getRepositories(definitions: ChartDefinition[]): Repository[] {
  const repositoryList = definitions
    .flatMap((value) => value.dependencies)
    .filter((dependency) => dependency.repository) // only keep non-local references --> if no repository is defined the chart will be searched in charts/<name>
    .filter((dependency) => !isAlias(dependency.repository)) // do not add registryAliases
    .filter((dependency) => !dependency.repository.startsWith('file:')) // skip repositories which are locally referenced
    .map((dependency) => {
      // remove additional keys to prevent interference at deduplication
      return {
        name: dependency.name,
        repository: dependency.repository,
      };
    });
  const dedup = new Set();
  return repositoryList.filter((el) => {
    const duplicate = dedup.has(el.repository);
    dedup.add(el.repository);
    return !duplicate;
  });
}

export function isAlias(repository: string): boolean {
  if (!repository) {
    return false;
  }
  return repository.startsWith('@') || repository.startsWith('alias:');
}

export function aliasRecordToRepositories(
  registryAliases: Record<string, string>,
): Repository[] {
  return Object.entries(registryAliases)
    .filter(([, url]) => /^(https?|oci):\/\/.+/.exec(url))
    .map(([alias, url]) => {
      return {
        name: alias,
        repository: url,
      };
    });
}

export function isFileInDir(dir: string, file: string): boolean {
  return upath.dirname(file) === dir;
}
