import path from "path";
import fs from "fs";
import { BaseAsset } from "../../model/assets-model.js";
import { isValidAsset } from "./asset-helper.js";
import { readMultiYaml } from "../common/yaml-helper.js";
import {
  readFile,
  isYamlFile,
  isDirectory,
  isDirOrFileExists,
  getSubDirectory,
  normalizePath,
} from "../common/fs-helper.js";
import { showWarning, showError } from "../common/message-helper.js";
import {
  DIRECTORY_DOESNT_EXIST,
  ERROR_IN_SEARCH_OF_ASSET,
  NO_ENTRIES_FOUND_FOR_KIND,
  NO_ASSET_METADATA,
  IS_FOUND_IN,
  INVALID_DIRECTORY,
} from "../../constants/message-constants.js";
import { COMMA } from "../../constants/app-constants.js";
import { equalsIgnoreCase } from "../common/data-helper.js";

const searchAssetByKind = async (
  kindToSearch: string,
  rootDirPath: string,
  projectNames: string
): Promise<Record<string, string>> => {
  const projectAssetMetadata: Record<string, string[]> = {};

  try {
    const projects = projectNames.split(COMMA);
    for (const project of projects) {
      const projectDirPath = getSubDirectory(rootDirPath, project);
      if (!isDirOrFileExists(projectDirPath) || !isDirectory(projectDirPath)) {
        showWarning(`${DIRECTORY_DOESNT_EXIST} ${projectDirPath}`);
        continue;
      }

      const matchingEntries = searchAssetByKindInDirectory(
        kindToSearch,
        projectDirPath
      );

      if (matchingEntries.length > 0) {
        for (const entry of matchingEntries) {
          const filePath = path.join(entry.parentPath, entry.name);
          extractKindMetadata(
            filePath,
            project,
            kindToSearch,
            projectAssetMetadata
          );
        }
      } else {
        showWarning(
          `${NO_ENTRIES_FOUND_FOR_KIND} - '${kindToSearch}' ${IS_FOUND_IN} '${projectDirPath}'`
        );
      }
    }
    return formatMetadataResult(projectAssetMetadata);
  } catch (error) {
    showError(
      `${ERROR_IN_SEARCH_OF_ASSET} ${kindToSearch}: ${(error as Error).message}`
    );
    throw error;
  }
};

const searchAssetByKindInDirectory = (
  kindToSearch: string,
  projectDirPath: string
): fs.Dirent[] => {
  if (!isDirOrFileExists(projectDirPath) || !isDirectory(projectDirPath)) {
    throw new Error(`${INVALID_DIRECTORY} ${projectDirPath}`);
  }

  try {
    const entries: fs.Dirent[] = fs.readdirSync(projectDirPath, {
      withFileTypes: true,
      recursive: true,
    });

    return entries.filter((entry) => {
      if (entry.isDirectory()) {
        return false;
      }
      if (!isYamlFile(entry.name)) {
        return false;
      }
      const assets = readMultiYaml<BaseAsset>(
        normalizePath(`${entry.parentPath}/${entry.name}`),
        readFile(entry.parentPath, entry.name)
      );

      return containsMatchingKind(assets, kindToSearch);
    });
  } catch (error) {
    showError(
      `${ERROR_IN_SEARCH_OF_ASSET} ${kindToSearch}: ${(error as Error).message}`
    );
    throw error;
  }
};

const containsMatchingKind = (
  assets: BaseAsset[],
  kindToSearch: string
): boolean => {
  for (const asset of assets) {
    if (isValidAsset(asset) && equalsIgnoreCase(kindToSearch, asset.kind)) {
      return true;
    }
  }
  return false;
};

const extractKindMetadata = (
  filePath: string,
  project: string,
  kindToSearch: string,
  projectAssetMetadata: Record<string, string[]>
): boolean => {
  try {
    const fileContent = readFile(
      path.dirname(filePath),
      path.basename(filePath)
    );
    const yamlContents = readMultiYaml<BaseAsset>(filePath, fileContent);
    const assetMetadata: string[] = [];
    yamlContents.forEach((yamlContent) => {
      if (isValidAsset(yamlContent)) {
        const kind = yamlContent.kind ? yamlContent.kind.toLowerCase() : "";
        if (kind === kindToSearch.toLowerCase()) {
          const metadata = getMetadata(yamlContent);
          assetMetadata.push(metadata);
        }
      }
    });
    if (assetMetadata.length > 0) {
      if (!projectAssetMetadata[project]) {
        projectAssetMetadata[project] = [];
      }
      projectAssetMetadata[project].push(...assetMetadata);
    }
    return assetMetadata.length > 0;
  } catch (error) {
    showError(
      `${error instanceof Error ? error.message : "Unknown error"}`
    );
    return false;
  }
};

const formatMetadataResult = (
  projectAssetMetadata: Record<string, string[]>
): Record<string, string> => {
  if (Object.keys(projectAssetMetadata).length === 0) {
    showError(NO_ASSET_METADATA);
  }
  const result: Record<string, string> = {};
  for (const [project, metadataArray] of Object.entries(projectAssetMetadata)) {
    result[project] = metadataArray.join(COMMA);
  }

  return result;
};

const getMetadata = (asset: BaseAsset): string => {
  const namespace = asset.metadata?.namespace || "";
  const name = asset.metadata?.name || "";
  const version = asset.metadata?.version || "";
  return `${namespace}:${name}:${version}`;
};

export { searchAssetByKind };
