/* eslint-disable jsdoc/require-jsdoc */
/*
 * Copyright (C) 2021 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import ts from 'typescript';
import {LightweightAliasDefinition} from './types/lightweight-alias-definition.js';
import {LightweightClassDefinition} from './types/lightweight-class-definition.js';
import {LightweightDefinition} from './types/lightweight-definition.js';
import {LightweightDefinitionKind} from './types/lightweight-definition-kind.js';
import {LightweightProject} from './types/lightweight-project.js';
import {LightweightType} from './types/lightweight-type.js';
import {keyBy} from '@openstapps/collection-utils';

/**
 * Creates a printable name of a type
 */
export function expandTypeValue(type: LightweightType): string | undefined {
  if (type.isArray) {
    return `${type.value}[]`;
  }
  if (isStringLiteralType(type)) {
    return `'${type.value}'`;
  }
  if (isUnionOrIntersectionType(type)) {
    return type.specificationTypes?.map(expandTypeValue).join(isUnionType(type) ? ' | ' : ' & ');
  }
  if (type.genericsTypes?.length === 0) {
    return `${type.value}<${type.genericsTypes?.map(expandTypeValue).join(', ')}>`;
  }

  return type.value?.toString();
}

export function definitionsOf(project: LightweightProject): Record<string, LightweightDefinition> {
  return keyBy(Object.values(project).flatMap(Object.values), it => it.name);
}

export function isPrimitiveType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.NonPrimitive) === 0;
}

export function isLiteralType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.Literal) !== 0;
}

export function isEnumLiteralType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.EnumLiteral) !== 0;
}

export function isStringLiteralType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.StringLiteral) !== 0;
}

export function isUnionOrIntersectionType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.UnionOrIntersection) !== 0;
}

export function isUnionType(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.Union) !== 0;
}

export function isLightweightClass(node?: LightweightDefinition): node is LightweightClassDefinition {
  return node?.kind === LightweightDefinitionKind.CLASS_LIKE;
}

export function isLightweightEnum(node?: LightweightDefinition): node is LightweightAliasDefinition {
  return node?.kind === LightweightDefinitionKind.ALIAS_LIKE;
}

export function isTypeVariable(type: {flags: ts.TypeFlags}): boolean {
  return (type.flags & ts.TypeFlags.TypeVariable) !== 0;
}
