/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: BUSL-1.1
 */

import RepositoryService from 'consul-ui/services/repository';
import dataSource from 'consul-ui/decorators/data-source';
import { inject as service } from '@ember/service';

function normalizePeerPayload(peerPayload, dc, partition) {
  const {
    StreamStatus: { LastHeartbeat, LastReceive, LastSend, ImportedServices, ExportedServices },
  } = peerPayload;

  return {
    ...peerPayload,
    LastHeartbeat,
    LastReceive,
    LastSend,
    ImportedServices,
    ExportedServices,
    Datacenter: dc,
    Partition: partition,
  };
}
export default class PeerService extends RepositoryService {
  @service store;

  getModelName() {
    return 'peer';
  }

  @dataSource('/:partition/:ns/:ds/exported-services/:name')
  async fetchExportedServices({ dc, ns, partition, name }, configuration, request) {
    return (
      await request`
      GET /v1/internal/ui/exported-services

      ${{
        peer: name,
      }}
    `
    )((headers, body, cache) => {
      const serviceSerializer = this.store.serializerFor('service');

      return this.store.push(
        serviceSerializer.createJSONApiDocumentFromServicesPayload(headers, body, dc)
      );
    });
  }

  @dataSource('/:partition/:ns/:dc/peering/token-for/:name/:externalAddresses')
  async fetchToken({ dc, ns, partition, name, externalAddresses }, configuration, request) {
    const ServerExternalAddresses =
      externalAddresses?.length > 0 ? externalAddresses.split(',') : [];

    return (
      await request`
      POST /v1/peering/token

      ${{
        PeerName: name,
        Partition: partition || undefined,
        ServerExternalAddresses,
      }}
    `
    )((headers, body, cache) => body);
  }

  @dataSource('/:partition/:ns/:dc/peers')
  async fetchAll({ dc, ns, partition }, { uri }, request) {
    return (
      await request`
      GET /v1/peerings

      ${{
        partition,
      }}
    `
    )((headers, body, cache) => {
      return {
        meta: {
          version: 2,
          interval: 10000,
          uri: uri,
        },
        body: body.map((item) => {
          return cache(
            normalizePeerPayload(item, dc, partition),
            (uri) => uri`peer:///${partition}/${ns}/${dc}/peer/${item.Name}`
          );
        }),
      };
    });
  }

  @dataSource('/:partition/:ns/:dc/peer-generate/')
  @dataSource('/:partition/:ns/:dc/peer-initiate/')
  @dataSource('/:partition/:ns/:dc/peer/:name')
  async fetchOne({ partition, ns, dc, name }, { uri }, request) {
    if (typeof name === 'undefined' || name === '') {
      const item = this.create({
        Datacenter: dc,
        Namespace: '',
        Partition: partition,
      });
      item.meta = { cacheControl: 'no-store' };
      return item;
    }
    return (
      await request`
      GET /v1/peering/${name}

      ${{
        partition,
      }}
    `
    )((headers, body, cache) => {
      // we can't easily use fragments as we are working around the serializer
      // layer
      const { StreamStatus } = body;

      if (StreamStatus) {
        if (StreamStatus.LastHeartbeat) {
          StreamStatus.LastHeartbeat = new Date(StreamStatus.LastHeartbeat);
        }
        if (StreamStatus.LastReceive) {
          StreamStatus.LastReceive = new Date(StreamStatus.LastReceive);
        }
        if (StreamStatus.LastSend) {
          StreamStatus.LastSend = new Date(StreamStatus.LastSend);
        }
      }

      return {
        meta: {
          version: 2,
          interval: 10000,
          uri: uri,
        },
        body: cache(
          normalizePeerPayload(body, dc, partition),
          (uri) => uri`peer:///${partition}/${ns}/${dc}/peer/${body.Name}`
        ),
      };
    });
  }

  async persist(item, request) {
    // mark it as ESTABLISHING ourselves as the request is successful
    // and we don't have blocking queries here to get immediate updates
    return (
      await request`
      POST /v1/peering/establish

      ${{
        PeerName: item.Name,
        PeeringToken: item.PeeringToken,
        Partition: item.Partition || undefined,
      }}
    `
    )((headers, body, cache) => {
      const partition = item.Partition;
      const ns = item.Namespace;
      const dc = item.Datacenter;
      return {
        meta: {
          version: 2,
        },
        body: cache(
          {
            ...item,
            State: 'ESTABLISHING',
          },
          (uri) => uri`peer:///${partition}/${ns}/${dc}/peer/${item.Name}`
        ),
      };
    });
  }

  async remove(item, request) {
    // soft delete
    // we just return the item we want to delete
    // but mark it as DELETING ourselves as the request is successful
    // and we don't have blocking queries here to get immediate updates
    return (
      await request`
      DELETE /v1/peering/${item.Name}
    `
    )((headers, body, cache) => {
      const partition = item.Partition;
      const ns = item.Namespace;
      const dc = item.Datacenter;
      return {
        meta: {
          version: 2,
        },
        body: cache(
          {
            ...item,
            State: 'DELETING',
          },
          (uri) => uri`peer:///${partition}/${ns}/${dc}/peer/${item.Name}`
        ),
      };
    });
  }
}
