/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: MPL-2.0
 */

import { set, get } from '@ember/object';
import ApplicationSerializer from './application';
import classic from 'ember-classic-decorator';
import { capitalize } from '@ember/string';
@classic
export default class VolumeSerializer extends ApplicationSerializer {
  attrs = {
    externalId: 'ExternalID',
  };

  embeddedRelationships = ['writeAllocations', 'readAllocations'];

  // Volumes treat Allocations as embedded records. Ember has an
  // EmbeddedRecords mixin, but it assumes an application is using
  // the REST serializer and Nomad does not.
  normalize(typeHash, hash) {
    hash.NamespaceID = hash.Namespace;

    hash.PlainId = hash.ID;

    // TODO These shouldn't hardcode `csi/` as part of the IDs,
    // but it is necessary to make the correct find requests and the
    // payload does not contain the required information to derive
    // this identifier.
    hash.ID = JSON.stringify([`csi/${hash.ID}`, hash.NamespaceID || 'default']);
    hash.PluginID = `csi/${hash.PluginID}`;

    // Populate read/write allocation lists from aggregate allocation list
    const readAllocs = hash.ReadAllocs || {};
    const writeAllocs = hash.WriteAllocs || {};

    hash.ReadAllocations = [];
    hash.WriteAllocations = [];

    if (hash.Allocations) {
      hash.Allocations.forEach(function (alloc) {
        const id = alloc.ID;
        if (id in readAllocs) {
          hash.ReadAllocations.push(alloc);
        }
        if (id in writeAllocs) {
          hash.WriteAllocations.push(alloc);
        }
      });
      delete hash.Allocations;
    }

    const normalizedHash = super.normalize(typeHash, hash);
    return this.extractEmbeddedRecords(
      this,
      this.store,
      typeHash,
      normalizedHash
    );
  }

  keyForRelationship(attr, relationshipType) {
    //Embedded relationship attributes don't end in IDs
    if (this.embeddedRelationships.includes(attr)) return capitalize(attr);
    return super.keyForRelationship(attr, relationshipType);
  }

  // Convert the embedded relationship arrays into JSONAPI included records
  extractEmbeddedRecords(serializer, store, typeHash, partial) {
    partial.included = partial.included || [];

    this.embeddedRelationships.forEach((embed) => {
      const relationshipMeta = typeHash.relationshipsByName.get(embed);
      const relationship = get(partial, `data.relationships.${embed}.data`);

      if (!relationship) return;

      // Create a sidecar relationships array
      const hasMany = new Array(relationship.length);

      // For each embedded allocation, normalize the allocation JSON according
      // to the allocation serializer.
      relationship.forEach((alloc, idx) => {
        const { data, included } = this.normalizeEmbeddedRelationship(
          store,
          relationshipMeta,
          alloc
        );

        // In JSONAPI, embedded records go in the included array.
        partial.included.push(data);
        if (included) {
          partial.included.push(...included);
        }

        // In JSONAPI, the main payload value is an array of IDs that
        // map onto the objects in the included array.
        hasMany[idx] = { id: data.id, type: data.type };
      });

      // Set the JSONAPI relationship value to the sidecar.
      const relationshipJson = { data: hasMany };
      set(partial, `data.relationships.${embed}`, relationshipJson);
    });

    return partial;
  }

  normalizeEmbeddedRelationship(store, relationshipMeta, relationshipHash) {
    const modelName = relationshipMeta.type;
    const modelClass = store.modelFor(modelName);
    const serializer = store.serializerFor(modelName);

    return serializer.normalize(modelClass, relationshipHash, null);
  }
}
