/* eslint-disable no-empty */
import {service} from '@loopback/core';
import {cloneDeep, filter, indexOf} from 'lodash';
import {addCondition} from '../../helper';
import {ObjectNode, ObjectTree} from '../../models';
import {EntityName} from '../../models/entity-name';
import {CurrentContext, ObjectTreeService} from '../../services';
import {InsideRestService} from '../../services/inside-rest/inside-rest.service';
import {ObjectNodeService} from '../../services/object-node/object-node.service';
import {ObjectTypeService} from '../../services/object-type.service';
import {TransientEntityService} from '../../services/transient-entity/transient-entity.service';
import {UriCompleteService} from '../../services/uri-complete/uri-complete.service';
import {
  CONTENT_FILE_PROVIDER,
  FILES_DIRECTORY_TYPE,
  FILE_DIRECTORY_REFERRER_TYPE,
  FILE_DIRECTORY_SELECTOR_TYPE,
} from './content-file.const';
import {ContentFileService} from './content-file.definition';

export class TransientFileService {
  constructor(
    @service(TransientEntityService)
    private transientEntityService: TransientEntityService,
    @service(InsideRestService)
    private insideRestService: InsideRestService,
    @service(UriCompleteService)
    private uriCompleteService: UriCompleteService,
    @service(ContentFileService)
    private contentFileService: ContentFileService,
    @service(ObjectTreeService)
    private objectTreeService: ObjectTreeService,
    @service(ObjectNodeService)
    private objectNodeService: ObjectNodeService,
    @service(ObjectTypeService)
    private objectTypeService: ObjectTypeService,
  ) {
    this.transientEntityService.registerTransientEntityTypeFunction(
      CONTENT_FILE_PROVIDER,
      TransientFileService.name,
      'Add referenced files field and its json schema definition. Add load Files method. Add create Directory method.',
      EntityName.objectNode,
      FILE_DIRECTORY_REFERRER_TYPE.name,
      this.completeFileDirectoryReferrerNode.bind(this),
    );
    this.transientEntityService.registerTransientEntityTypeFunction(
      CONTENT_FILE_PROVIDER,
      TransientFileService.name,
      'Add load Files method definition to load several files in one operation and reset files position and date info method',
      EntityName.objectNode,
      FILES_DIRECTORY_TYPE.name,
      this.completeFileDirectoryTypeNode.bind(this),
    );
    /*
    this.transientEntityService.registerTransientEntityTypeFunction(
      CONTENT_FILE_PROVIDER,
      TransientFileService.name,
      'Add all files of the directory as referenced files',
      EntityName.objectTree,
      DISPLAYED_FILE_DIRECTORY_TYPE.name,
      this.completeDisplayedFileDirectoryTree.bind(this),
    );
    this.transientEntityService.registerTransientEntityTypeFunction(
      CONTENT_FILE_PROVIDER,
      TransientFileService.name,
      'Add all files of the all referenced directories',
      EntityName.objectTree,
      FILE_DIRECTORY_CONCATENATION_TYPE.name,
      this.completeFileDirectoryConcatenationTree.bind(this),
    );

    this.transientEntityService.registerTransientEntityTypeFunction(
      CONTENT_FILE_PROVIDER,
      TransientEntityService.name,
      'Add file EXIF info and reset info method',
      EntityName.objectNode,
      FILE_TYPE.name,
      this.completeFileTypeNode.bind(this),
    );
    */
  }

  /*
  public async completeDisplayedFileDirectoryTree(
    entity: ObjectTree,
    ctx: CurrentContext,
  ) {
    const files: ObjectTree[] = (
      await this.objectTreeService.getChildrenByImplementedTypeId(entity)
    )[FILE_TYPE.name];
    entity.files = files ? files : [];
  }
  */

  /*
  public async completeFileDirectoryConcatenationTree(
    entity: ObjectTree,
    ctx: CurrentContext,
  ) {
    entity.files = [];
    if (
      (
        await this.objectTypeService.getImplementedTypes(
          entity.treeNode.objectTypeId,
        )
      ).includes(FILE_DIRECTORY_CONCATENATION_ALL_DEFAULT_TYPE.name) &&
      !entity.treeNode.selectDirectories
    ) {
      const directoryTrees = await this.objectNodeService.searchTreesOfNamespaceId(
        entity.treeNode.parentNamespaceId,
        FILES_DIRECTORY_TYPE.name,
      );
      entity.treeNode.selectedGalleriesObjectTreeUris = directoryTrees.map(
        (directoryNode) =>
          this.uriCompleteService.getUri(
            EntityName.objectTree,
            directoryNode.id as string,
            ctx,
          ),
      );
    }
    if (entity.treeNode.selectedGalleriesObjectTreeUris) {
      for (const directoryTreeUri of entity.treeNode
        .selectedGalleriesObjectTreeUris) {
        const directoryTree: ObjectTree = await this.insideRestService.read<ObjectTree>(
          directoryTreeUri,
          ctx,
        );
        if (directoryTree.files) {
          entity.files.push(...directoryTree.files);
        }
      }
    }
  }
  */

  private async completeFileDirectoryTypeNode(
    objectNode: ObjectNode,
    ctx: CurrentContext,
  ) {
    if (!objectNode.entityCtx) {
      objectNode.entityCtx = {entityType: EntityName.objectNode};
    }
    if (!objectNode.entityCtx?.actions) {
      objectNode.entityCtx.actions = {};
    }
    if (!objectNode.entityCtx.actions.methods) {
      objectNode.entityCtx.actions.methods = [];
    }
    await this.addLoadFilesJsonSchemaMethod(objectNode, ctx);
  }

  public async getFileDirectoriesParents(
    objectNode: ObjectNode,
  ): Promise<ObjectNode[]> {
    const directoriesType: string[] = await this.objectTypeService.getParentTypesOfImplementingType(
      FILES_DIRECTORY_TYPE.name,
    );
    return this.objectNodeService.searchByParentNamespaceId(
      objectNode.parentNamespaceId,
      {
        objectTypeIds: directoriesType,
      },
    );
  }

  public async addCreateFilesDirectoryJsonSchemaMethod(
    objectNode: ObjectNode,
    ctx: CurrentContext,
  ) {
    if (!objectNode.entityCtx) {
      objectNode.entityCtx = {entityType: EntityName.objectNode};
    }
    if (!objectNode.entityCtx?.actions) {
      objectNode.entityCtx.actions = {};
    }
    if (!objectNode.entityCtx.actions.methods) {
      objectNode.entityCtx.actions.methods = [];
    }
    const fileGalleries: ObjectNode[] = await this.getFileDirectoriesParents(
      objectNode,
    );
    if (fileGalleries && 0 < fileGalleries.length) {
      objectNode.entityCtx.actions.methods.push({
        methodId: 'createDirectory',
        methodName: 'Add files',
        actionName: 'Load files',
        parameters: {
          type: 'object',
          properties: {
            useExistingDirectory: {
              type: 'boolean',
              title: 'Use existing directory',
            },
            name: {
              type: 'string',
              title: 'Directory name',
              'x-schema-form': {
                type: 'conditional-text',
                conditionalValue: {
                  title: 'Specify a directory name',
                  defaultValue: `return ${JSON.stringify(objectNode.title)}`,
                },
              },
            },
          },
        },
        handlebarsMethodSampling: `[
          {{^useExistingDirectory}}
            {
              "methodId": "createDirectory",
              "parameters":{
                "name": {{&json name}},
                "parentId": {{&json parentId}}
              }
            }
          {{/useExistingDirectory}}
          {{#useExistingDirectory}}
            {
              "methodId": "configureDirectory",
              "parameters":{
                "fileDirectoryObjectTreeId": {{&json fileDirectoryObjectTreeId}}
              }
            }
          {{/useExistingDirectory}}
          {{#files}},
          {
            "methodId":"load",
            "parameters": {
              "title": {{&json ../title}},
              "files": [
                {{&json this}}
              ]
            }
          }{{/files}}
          ]`,
        icon: 'far fa-files',
      });
      const properties =
        objectNode.entityCtx.actions.methods[
          objectNode.entityCtx.actions.methods.length - 1
        ].parameters.properties;
      if (1 < fileGalleries.length) {
        properties.parentId = {
          type: 'string',
          title: 'Galleries parent',
          required: true,
          oneOf: fileGalleries.map((directory) => ({
            enum: [directory.id],
            title: directory.name,
          })),
        };
      }
      if (
        objectNode.entityCtx?.jsonSchema?.properties.fileDirectoryObjectTreeId
          ?.oneOf?.length > 0
      ) {
        addCondition('true !== model.useExistingDirectory', properties.name);
        if (properties.parentId) {
          addCondition(
            'true !== model.useExistingDirectory',
            properties.parentId,
          );
        }
        properties.fileDirectoryObjectTreeId = cloneDeep(
          objectNode.entityCtx?.jsonSchema?.properties
            .fileDirectoryObjectTreeId,
        );
        properties.fileDirectoryObjectTreeId.required = true;
        addCondition(
          'true === model.useExistingDirectory',
          properties.fileDirectoryObjectTreeId,
        );
      } else {
        delete properties.useExistingDirectory;
      }

      properties.title = {
        type: 'string',
        title: 'Displayed name',
        'x-schema-form': {
          type: 'conditional-text',
          conditionalValue: {
            title: 'Specify a label for files',
            defaultValue: `return ${JSON.stringify(objectNode.title)}`,
          },
        },
        default: objectNode.title,
      };
      addCondition(
        '(true === model.useExistingDirectory && model.fileDirectoryObjectTreeId) || !model.useExistingDirectory',
        properties.title,
      );

      properties.files = {
        type: 'array',
        'x-schema-form': {
          type: 'files',
        },
        items: (await this.contentFileService.getContentDefinition()).properties
          .contentFile,
      };
      addCondition(
        '(true === model.useExistingDirectory && model.fileDirectoryObjectTreeId) || !model.useExistingDirectory',
        properties.files,
      );
    }
  }

  public async addLoadFilesJsonSchemaMethod(
    objectNode: ObjectNode,
    ctx: CurrentContext,
  ) {
    if (!objectNode.entityCtx) {
      objectNode.entityCtx = {entityType: EntityName.objectNode};
    }
    if (!objectNode.entityCtx?.actions) {
      objectNode.entityCtx.actions = {};
    }
    if (!objectNode.entityCtx.actions.methods) {
      objectNode.entityCtx.actions.methods = [];
    }
    objectNode.entityCtx.actions.methods.push({
      methodId: 'load',
      methodName: 'Add files',
      actionName: 'Load files',
      parameters: {
        type: 'object',
        properties: {
          title: {
            type: 'string',
            title: 'Displayed name',
            'x-schema-form': {
              type: 'conditional-text',
              conditionalValue: {
                title: 'Specify a label for files',
                defaultValue: `return ${JSON.stringify(objectNode.title)}`,
              },
            },
            default: objectNode.title,
          },
          files: {
            type: 'array',

            'x-schema-form': {
              type: 'files',
            },

            items: (await this.contentFileService.getContentDefinition())
              .properties.contentFile,
          },
        },
      },
      handlebarsMethodSampling: `[{{#files}}
          {
            "methodId":"load",
            "parameters": {
              "title": {{&json ../title}},
              "files": [
                {{&json this}}
              ]
            }
          }{{#unless @last}},{{/unless}}{{/files}}
        ]`,
      icon: 'far fa-files',
    });
  }

  public async completeFileDirectoryReferrerNode(
    objectNode: ObjectNode,
    ctx: CurrentContext,
  ) {
    const files = [];
    if (
      !objectNode.fileDirectoryTree &&
      objectNode.fileDirectoryObjectTreeUri
    ) {
      try {
        objectNode.fileDirectoryTree = (await this.insideRestService.read(
          objectNode.fileDirectoryObjectTreeUri,
          ctx,
        )) as ObjectTree;
      } catch (error) {}
    }
    if (objectNode.fileDirectoryTree?.children) {
      for (const file of objectNode.fileDirectoryTree.children) {
        files.push(file);
      }
    }
    objectNode.files = files;

    if (
      -1 <
        indexOf(
          objectNode.entityCtx?.implementedTypes,
          FILE_DIRECTORY_SELECTOR_TYPE.name,
        ) &&
      objectNode.entityCtx?.jsonSchema?.properties?.selectedFiles
    ) {
      if (0 < files.length) {
        objectNode.entityCtx.jsonSchema.properties.selectedFiles.items.enumNames = [];
        objectNode.entityCtx.jsonSchema.properties.selectedFiles.items.enum = [];
        for (const file of files) {
          if (
            -1 ===
            indexOf(
              objectNode.entityCtx.jsonSchema.properties.selectedFiles.items
                .enum,
              file.treeNode.name,
            )
          ) {
            objectNode.entityCtx.jsonSchema.properties.selectedFiles.items.enum.push(
              file.treeNode.name,
            );
            objectNode.entityCtx.jsonSchema.properties.selectedFiles.items.enumNames.push(
              file.treeNode.name +
                '<span class="fileSelect"><img onclick="showImg(\'' +
                file.treeNode.contentFileUri +
                '\')" src="' +
                file.treeNode.contentFileUri +
                '" ></span>',
            );
          }
        }
        if (objectNode.selectedFiles) {
          objectNode.files = filter(
            files,
            (file) =>
              -1 < indexOf(objectNode.selectedFiles, file.treeNode.name),
          );
        } else {
          objectNode.selectedFiles =
            objectNode.entityCtx.jsonSchema.properties.selectedFiles.items.enum;
        }

        addCondition(
          "model.fileDirectoryObjectTreeId=='" +
            objectNode.fileDirectoryObjectTreeId +
            "'",
          objectNode.entityCtx.jsonSchema.properties.selectedFiles,
        );
      } else {
        delete objectNode.entityCtx.jsonSchema.properties.selectedFiles;
      }
    }
    if (objectNode.fileDirectoryTree) {
      await this.addLoadFilesJsonSchemaMethod(objectNode, ctx);
    } else {
      await this.addCreateFilesDirectoryJsonSchemaMethod(objectNode, ctx);
    }
  }
}
