// TODO: Properly import bff-service signature from npm module
import { Service } from '@devsun/arm-bff/dist/lib/database/models/mongo/pgs-schema-util/Services';
import { Schema } from '@devsun/arm-bff/dist/lib/database/models/mongo/pgs-schema-util/Schemas';
import { DocumentType } from '@typegoose/typegoose';
import { uid } from 'quasar';
import axios from 'axios';
import { MetaClassSchemaRaw } from '@devsun/eva-models/dist/models/Schema';

export interface DownloadDictsParams {
  enums: EnumCollection;
  archiveName: string;
}

export type EnumCollection = EnumCollectionItem[];

export interface EnumCollectionItem {
  enum: ModelEnumItem[];
  name: string;
  title?: string;
  fileName: string;
}

export interface ModelEnumItem {
  key: string;
  value: string;
}
interface BFFService {
  createService(serviceData: Service): Promise<DocumentType<Service>>;
  getServiceById(id: string): Promise<DocumentType<Service>>;
  getSchemasByService(serviceId: string): Promise<Array<DocumentType<Schema>>>;
  updateService(
    service: Partial<Service>,
    id: string
  ): Promise<DocumentType<Service>>;
  json2xsd(json: JSON): Promise<BlobPart>;
  downloadDicts({ enums, archiveName }: DownloadDictsParams): Promise<BlobPart>;
  createEmptySchema(
    schemaModel: Omit<Schema, 'modified' | 'created' | 'draft'>
  ): Promise<DocumentType<Schema>>;
  updateSchema(
    schemaDocument: DocumentType<Schema>,
    updatedSchema: MetaClassSchemaRaw
  ): Promise<DocumentType<Schema>>;
  deleteSchema(
    schemaId: string,
    serviceId: string
  ): Promise<Array<DocumentType<Schema>>>;
}

export class ModelerService implements BFFService {
  async createService(serviceData: Service): Promise<DocumentType<Service>> {
    const serviceDoc = {
      _id: serviceData.serviceRegistryID || uid(),
      ...serviceData,
    };
    const serviceKey = serviceDoc._id;
    const serviceENC = JSON.stringify(serviceDoc);

    sessionStorage.setItem(serviceKey, serviceENC);

    return serviceDoc as DocumentType<Service>;
  }

  async getServiceById(id: string): Promise<DocumentType<Service>> {
    const serviceENC = sessionStorage.getItem(id);

    if (!serviceENC) throw new Error();

    return JSON.parse(serviceENC);
  }

  async getSchemasByService(
    serviceId: string
  ): Promise<Array<DocumentType<Schema>>> {
    const service = await this.getServiceById(serviceId);
    const schemas = service.schemas;

    return schemas ? JSON.parse(schemas) : [];
  }

  async updateService(
    partial: Partial<Service>,
    serviceId: string
  ): Promise<DocumentType<Service>> {
    const service = await this.getServiceById(serviceId);

    for (const [key, value] of Object.entries(partial)) {
      // @ts-ignore
      service[key] = value;
    }

    sessionStorage.setItem(service._id, JSON.stringify(service));

    return service;
  }

  async json2xsd(json: JSON): Promise<BlobPart> {
    return axios
      .request({
        url: 'jsonToXsd',
        baseURL: `${process.env.VUE_APP_PROJECT_PATH}/service/`,
        data: json,
        method: 'POST',
        responseType: 'arraybuffer',
      })
      .then(({ data }) => data);
  }

  async downloadDicts({
    enums,
    archiveName,
  }: DownloadDictsParams): Promise<BlobPart> {
    return axios
      .request({
        url: 'download-dicts',
        baseURL: `${process.env.VUE_APP_PROJECT_PATH}/service/`,
        method: 'POST',
        responseType: 'arraybuffer',
        data: {
          enums,
          archiveName,
        },
      })
      .then(({ data }) => data);
  }

  async createEmptySchema(
    schemaModel: Omit<Schema, 'modified' | 'created' | 'draft'>
  ): Promise<DocumentType<Schema>> {
    const schemaDoc = {
      _id: uid(),
      created: new Date().getTime(),
      modified: new Date().getTime(),
      ...schemaModel,
    };

    const service = await this.getServiceById(schemaDoc.serviceId);

    // @ts-ignore
    const schemas = JSON.parse(service.schemas || null);

    if (!schemas) {
      service.schemas = JSON.stringify([schemaDoc]);
    } else {
      schemas.push(schemaDoc);

      service.schemas = JSON.stringify(schemas);
    }

    this.updateService({ schemas: service.schemas }, schemaDoc.serviceId);

    return schemaDoc as DocumentType<Schema>;
  }

  async updateSchema(
    schemaDoc: DocumentType<Schema>,
    updatedSchema: MetaClassSchemaRaw
  ): Promise<DocumentType<Schema>> {
    const schemas = await this.getSchemasByService(schemaDoc.serviceId);
    const currentSchemaDoc = schemas.find(
      (schemaDocIt) => schemaDocIt._id === schemaDoc._id
    );

    if (currentSchemaDoc) {
      currentSchemaDoc.fgSchema = JSON.stringify(updatedSchema);
      currentSchemaDoc.name = updatedSchema.name;
      currentSchemaDoc.label = updatedSchema.label;
      currentSchemaDoc.modified = new Date().getTime();
    }

    await this.updateService(
      { schemas: JSON.stringify(schemas) },
      schemaDoc.serviceId
    );

    return currentSchemaDoc || schemaDoc;
  }

  async deleteSchema(
    schemaId: string,
    serviceId: string
  ): Promise<Array<DocumentType<Schema>>> {
    const schemas = await this.getSchemasByService(serviceId);
    const updated = schemas.filter((schemaIT) => schemaIT._id !== schemaId);

    await this.updateService({ schemas: JSON.stringify(updated) }, serviceId);

    return updated;
  }
}
