import { Obj } from "@intelligentgraphics/ig.utilities";
import { ConfigResolver, ArticleUtil } from "./Utilities";
import BaseLogger from "./Logger";
const Logger = BaseLogger.createSubLogger("ArticleManager");
export const createArticleManager = (serviceManager, articleState, articleQuery, operationResultHandler, applicationState) => {
    const createArticleFromCatalogItem = async ({ catalogItem, source, additionalInfo, ...passThrough }) => {
        const backend = await serviceManager.getSourceBackend(source);
        if (backend === undefined) {
            throw new Error(`No backend has been registered for "${source}", no article number can be created.`);
        }
        const articleNumber = backend.createArticleNumber(catalogItem);
        const params = {
            ...passThrough,
            source,
            articleNumber: {
                ...articleNumber,
                ...additionalInfo,
            },
        };
        const initialConfiguration = ConfigResolver.resolveInitialConfiguration(params, articleQuery, applicationState.getValue("selection"));
        const results = await serviceManager
            .getOperatorForSourceId(source)
            .addArticle({
            ...params,
            initialConfiguration,
        });
        await operationResultHandler.processOperationResults(results);
        return results[0].comId;
    };
    const createArticleFromArticleNumber = async (params) => {
        const creationResults = await serviceManager
            .getOperatorForSourceId(params.source)
            .addArticle({
            ...params,
            initialConfiguration: ConfigResolver.resolveInitialConfiguration(params, articleQuery, applicationState.getValue("selection")),
        });
        await operationResultHandler.processOperationResults(creationResults);
        return creationResults[0].comId;
    };
    const changeArticleProperty = (comId, propertyKey, newValue) => changeProperties([comId], { [propertyKey]: newValue });
    const changeArticleProperties = (comId, changes) => changeProperties([comId], changes);
    const changeProperty = (comIds, propertyKey, newValue) => changeProperties(comIds, { [propertyKey]: newValue });
    const changeProperties = async (comIds, changes) => {
        const comIdsBySources = groupArticlesBySource(comIds);
        const results = [];
        await Promise.all(Array.from(comIdsBySources).map(([sourceId, comIds]) => serviceManager
            .getOperatorForSourceId(sourceId)
            .configureArticles(comIds, changes)
            .then(sourceResults => results.push(...sourceResults))));
        await operationResultHandler.processOperationResults(results);
    };
    const cloneArticle = async (params) => {
        const article = articleQuery.getArticleByComId(params.comId);
        if (article === undefined) {
            throw new Error(`Can not clone unknown article "${params.comId}"`);
        }
        const cloneResults = await serviceManager
            .getOperatorForComId(params.comId)
            .cloneArticle(params);
        await operationResultHandler.processOperationResults(cloneResults);
        return cloneResults[0].comId;
    };
    const deleteArticle = (comId) => deleteArticles([comId]);
    const deleteArticles = async (comIds) => {
        const deleteTargets = [];
        for (const comId of comIds) {
            const article = articleQuery.getArticleByComId(comId);
            if (article === undefined) {
                Logger.warn(`Article with comId "${comId}" does not exist. Skipping deletion`);
                continue;
            }
            deleteTargets.push(comId);
        }
        const deleteResults = await Promise.all(deleteTargets.map(comId => serviceManager.getOperatorForComId(comId).deleteArticle(comId)));
        const flattenedResults = [];
        for (const results of deleteResults) {
            flattenedResults.push(...results);
        }
        await operationResultHandler.processOperationResults(flattenedResults);
    };
    const createArticlePropertySetter = (property) => (comId, value) => articleState.updateArticleProperty(comId, property, value);
    const setArticlePlacement = (comId, placement) => {
        const currentPlacement = articleQuery.getArticlePlacement(comId);
        if ((currentPlacement === undefined) === (placement === undefined) &&
            placement !== undefined &&
            Obj.shallowEqual(placement.position, currentPlacement.position) &&
            Obj.shallowEqual(placement.rotation, currentPlacement.rotation)) {
            return;
        }
        articleState.updateArticleProperty(comId, "placement", placement);
    };
    const groupArticlesBySource = (comIds) => {
        const comIdsBySources = new Map();
        for (const comId of comIds) {
            const sourceId = articleQuery.getArticleSource(comId);
            let sourceGroup = comIdsBySources.get(sourceId);
            if (sourceGroup === undefined) {
                sourceGroup = [];
                comIdsBySources.set(sourceId, sourceGroup);
            }
            sourceGroup.push(comId);
        }
        return comIdsBySources;
    };
    const getGfxRepresentation = (comId, options) => {
        ArticleUtil.getArticleOrThrow(articleQuery, comId);
        const operator = serviceManager.getOperatorForComId(comId);
        return operator.getGfxRepresentation(comId, options);
    };
    return {
        changeProperties,
        changeProperty,
        createArticleFromArticleNumber,
        createArticleFromCatalogItem,
        changeArticleProperties,
        changeArticleProperty,
        deleteArticle,
        deleteArticles,
        cloneArticle,
        setArticleData: createArticlePropertySetter("data"),
        setArticleGfxId: createArticlePropertySetter("gfxId"),
        setArticlePlacement,
        getGfxRepresentation,
    };
};
