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

import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import ApplicationSerializer from './application';
import classic from 'ember-classic-decorator';

const taskGroupFromJob = (job, taskGroupName) => {
  const taskGroups = job && job.TaskGroups;
  const taskGroup =
    taskGroups && taskGroups.find((group) => group.Name === taskGroupName);
  return taskGroup ? taskGroup : null;
};

const merge = (tasks) => {
  const mergedResources = {
    Cpu: { CpuShares: 0 },
    Memory: { MemoryMB: 0 },
    Disk: { DiskMB: 0 },
  };

  return tasks.reduce((resources, task) => {
    resources.Cpu.CpuShares += (task.Cpu && task.Cpu.CpuShares) || 0;
    resources.Memory.MemoryMB += (task.Memory && task.Memory.MemoryMB) || 0;
    resources.Disk.DiskMB += (task.Disk && task.Disk.DiskMB) || 0;
    return resources;
  }, mergedResources);
};

@classic
export default class AllocationSerializer extends ApplicationSerializer {
  @service system;

  attrs = {
    taskGroupName: 'TaskGroup',
    states: 'TaskStates',
  };

  separateNanos = ['CreateTime', 'ModifyTime'];

  normalize(typeHash, hash) {
    // Transform the map-based TaskStates object into an array-based
    // TaskState fragment list
    const states = hash.TaskStates || {};
    hash.TaskStates = Object.keys(states)
      .sort()
      .map((key) => {
        const state = states[key] || {};
        const summary = { Name: key };
        Object.keys(state).forEach(
          (stateKey) => (summary[stateKey] = state[stateKey])
        );
        summary.Resources =
          hash.AllocatedResources && hash.AllocatedResources.Tasks[key];
        return summary;
      });

    hash.JobVersion =
      hash.JobVersion != null ? hash.JobVersion : get(hash, 'Job.Version');

    hash.PlainJobId = hash.JobID;
    hash.Namespace = hash.Namespace || get(hash, 'Job.Namespace') || 'default';
    hash.JobID = JSON.stringify([hash.JobID, hash.Namespace]);

    hash.RescheduleEvents = (hash.RescheduleTracker || {}).Events;

    hash.IsMigrating = (hash.DesiredTransition || {}).Migrate;

    // API returns empty strings instead of null
    hash.PreviousAllocationID = hash.PreviousAllocation
      ? hash.PreviousAllocation
      : null;
    hash.NextAllocationID = hash.NextAllocation ? hash.NextAllocation : null;
    hash.FollowUpEvaluationID = hash.FollowupEvalID
      ? hash.FollowupEvalID
      : null;

    hash.PreemptedAllocationIDs = hash.PreemptedAllocations || [];
    hash.PreemptedByAllocationID = hash.PreemptedByAllocation || null;
    hash.WasPreempted = !!hash.PreemptedByAllocationID;

    const shared = hash.AllocatedResources && hash.AllocatedResources.Shared;
    hash.AllocatedResources =
      hash.AllocatedResources &&
      merge(Object.values(hash.AllocatedResources.Tasks));
    if (shared) {
      hash.AllocatedResources.Ports = shared.Ports;
      hash.AllocatedResources.Networks = shared.Networks;
    }

    // The Job definition for an allocation is only included in findRecord responses.
    hash.AllocationTaskGroup = !hash.Job
      ? null
      : taskGroupFromJob(hash.Job, hash.TaskGroup);

    return super.normalize(typeHash, hash);
  }
}
