import type { IContentItem } from "@kontent-ai/delivery-sdk";
import type { ContentTypeModels } from "@kontent-ai/management-sdk";
import Chalk from "chalk";
import { itemsConfig } from "../../config.js";
import { wrapComment } from "../../core/comment.utils.js";
import type { GeneratedSet } from "../../core/core.models.js";
import { resolveCase } from "../../core/resolvers.js";

export interface ItemGeneratorConfig {
	readonly environmentData: {
		readonly items: readonly Readonly<IContentItem>[];
		readonly types: readonly Readonly<ContentTypeModels.ContentType>[];
	};
}

export function getItemsGenerator(config: ItemGeneratorConfig) {
	const getItemCodenameType = (typeCodename: string, items: readonly Readonly<IContentItem>[]): string => {
		return `export type ${resolveCase(typeCodename, "pascalCase")}Codenames = ${items.map((item) => `'${item.system.codename}'`).join(" | ")};`;
	};

	const getItemCodenamesProp = (typeCodename: string, items: readonly Readonly<IContentItem>[]): string => {
		const values = items.reduce((code, item, index) => {
			const isLast = index === items.length - 1;

			return `${code}\n
                ${wrapComment(item.system.name)}
                ${item.system.codename}: {
                    codename: '${item.system.codename}',
                    id: '${item.system.id}'
                }${!isLast ? ",\n" : ""}`;
		}, "");

		return `export const ${typeCodename}Items = {
                    ${values}
                } as const;`;
	};

	const groupItemsByType = (items: readonly Readonly<IContentItem>[]): Map<string, readonly Readonly<IContentItem>[]> => {
		return items.reduce<Map<string, readonly Readonly<IContentItem>[]>>((itemsByType, item) => {
			const existingGroupItems = itemsByType.get(item.system.type);
			if (existingGroupItems) {
				itemsByType.set(item.system.type, [...existingGroupItems, item]);
			} else {
				itemsByType.set(item.system.type, [item]);
			}

			return itemsByType;
		}, new Map<string, readonly Readonly<IContentItem>[]>());
	};

	return {
		getItemFiles(): GeneratedSet {
			return {
				folderName: itemsConfig.itemsFolderName,
				files: Array.from(groupItemsByType(config.environmentData.items)).map(([typeCodename, items]) => {
					const type = config.environmentData.types.find((t) => t.codename.toLowerCase() === typeCodename.toLowerCase());

					if (!type) {
						throw new Error(`Type with codename '${Chalk.red(typeCodename)}' not found`);
					}

					return {
						filename: `${typeCodename}.items.ts`,
						text: `${wrapComment("Object representing identifiers of available items", {
							lines: [
								{
									name: "Type name",
									value: type.name,
								},
								{
									name: "Codename",
									value: type.codename,
								},
								{
									name: "Id",
									value: type.id,
								},
							],
						})}
                    ${getItemCodenamesProp(typeCodename, items)}`,
					};
				}),
			};
		},
		getCodenameFiles(): GeneratedSet {
			return {
				folderName: itemsConfig.codenamesFolderName,
				files: Array.from(groupItemsByType(config.environmentData.items)).map(([typeCodename, items]) => {
					const type = config.environmentData.types.find((t) => t.codename.toLowerCase() === typeCodename.toLowerCase());

					if (!type) {
						throw new Error(`Type with codename '${Chalk.red(typeCodename)}' not found`);
					}

					return {
						filename: `${typeCodename}.codenames.ts`,
						text: `${wrapComment("Type representing available item codenames", {
							lines: [
								{
									name: "Type name",
									value: type.name,
								},
								{
									name: "Type codename",
									value: type.codename,
								},
							],
						})}
                    ${getItemCodenameType(typeCodename, items)}`,
					};
				}),
			};
		},
	};
}
