/**
 * Copyright IBM Corp. 2016, 2025
 * SPDX-License-Identifier: BUSL-1.1
 */

import { setupTest } from 'ember-qunit';
import { module, test } from 'qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { associationsResponse } from 'vault/mirage/handlers/sync';
import sinon from 'sinon';

module('Unit | Adapter | sync | association', function (hooks) {
  setupTest(hooks);
  setupMirage(hooks);

  hooks.beforeEach(function () {
    this.store = this.owner.lookup('service:store');
    this.pagination = this.owner.lookup('service:pagination');

    this.params = [
      { type: 'aws-sm', name: 'us-west-1' },
      { type: 'gh', name: 'baz' },
    ];
    this.params.forEach((params) => {
      this.server.create('sync-destination', params.type, { name: params.name });
      const association = this.server.create('sync-association', {
        ...params,
        mount: 'foo',
        secret_name: 'bar',
      });
      if (!this.association) {
        this.association = association;
      }
    });

    this.newModel = this.store.createRecord('sync/association', {
      destinationType: 'aws-sm',
      destinationName: 'us-west-1',
      mount: 'foo',
      secretName: 'bar',
    });
  });

  test('it should make request to correct endpoint when querying', async function (assert) {
    assert.expect(2);

    this.server.get('/sys/sync/destinations/:type/:name/associations', (schema, req) => {
      // list query param not required for this endpoint
      assert.deepEqual(req.queryParams, {}, 'query params stripped from request');
      assert.deepEqual(req.params, this.params[0], 'request is made to correct endpoint when querying');
      return associationsResponse(schema, req);
    });

    await this.pagination.lazyPaginatedQuery('sync/association', {
      responsePath: 'data.keys',
      page: 1,
      destinationType: 'aws-sm',
      destinationName: 'us-west-1',
    });
  });

  test('it should make request to correct endpoint for queryAll associations', async function (assert) {
    assert.expect(3);

    this.server.get('/sys/sync/associations', (schema, req) => {
      assert.ok(true, 'request is made to correct endpoint for queryAll');
      assert.propEqual(req.queryParams, { list: 'true' }, 'query params include list: true');
      return {
        data: {
          key_info: {},
          keys: [],
          total_associations: 5,
          total_secrets: 7,
        },
      };
    });

    const response = await this.store.adapterFor('sync/association').queryAll();
    const expected = { total_associations: 5, total_secrets: 7 };
    assert.deepEqual(response, expected, 'It returns correct values for queryAll');
  });

  test('it should make request to correct endpoint when creating record', async function (assert) {
    assert.expect(2);

    this.server.post('/sys/sync/destinations/:type/:name/associations/set', (schema, req) => {
      assert.deepEqual(req.params, this.params[0], 'request is made to correct endpoint when querying');
      assert.deepEqual(
        JSON.parse(req.requestBody),
        { mount: 'foo', secret_name: 'bar' },
        'Correct payload is sent when creating association'
      );
      return associationsResponse(schema, req);
    });

    await this.newModel.save({ adapterOptions: { action: 'set' } });
  });

  test('it should make request to correct endpoint when updating record', async function (assert) {
    assert.expect(2);

    this.server.post('/sys/sync/destinations/:type/:name/associations/remove', (schema, req) => {
      assert.deepEqual(req.params, this.params[0], 'request is made to correct endpoint when querying');
      assert.deepEqual(
        JSON.parse(req.requestBody),
        { mount: 'foo', secret_name: 'bar' },
        'Correct payload is sent when removing association'
      );
      return associationsResponse(schema, req);
    });

    this.store.pushPayload('sync/association', {
      modelName: 'sync/association',
      destinationType: 'aws-sm',
      destinationName: 'us-west-1',
      mount: 'foo',
      secret_name: 'bar',
      sync_status: 'SYNCED',
      id: 'foo/bar',
    });
    const model = this.store.peekRecord('sync/association', 'foo/bar');

    await model.save({ adapterOptions: { action: 'remove' } });
  });

  test('it should parse response from set/remove request', async function (assert) {
    this.server.post('/sys/sync/destinations/:type/:name/associations/set', associationsResponse);

    const adapter = this.store.adapterFor('sync/association');
    // mock snapshot
    const snapshot = {
      attributes() {
        return { destinationName: 'us-west-1', destinationType: 'aws-sm' };
      },
      serialize() {
        return { mount: 'foo', secret_name: 'bar' };
      },
      adapterOptions: { action: 'set' },
    };
    const response = await adapter._setOrRemove(this.store, { modelName: 'sync/association' }, snapshot);
    const { accessor, mount, secret_name, sync_status, name, type, updated_at } = this.association;
    const expected = {
      id: 'foo/bar',
      accessor,
      mount,
      secret_name,
      sync_status,
      updated_at,
      destinationType: type,
      destinationName: name,
    };

    assert.deepEqual(
      response,
      expected,
      'Custom create/update record method makes request and parses response'
    );
  });

  test('it should throw error if save action is not passed in adapterOptions', async function (assert) {
    assert.expect(1);

    try {
      await this.newModel.save();
    } catch (e) {
      assert.strictEqual(
        e.message,
        "Assertion Failed: action type of set or remove required when saving association => association.save({ adapterOptions: { action: 'set' }})"
      );
    }
  });

  test('it should fetch and normalize many associations for fetchByDestinations', async function (assert) {
    assert.expect(3);

    const handler = this.server.get('/sys/sync/destinations/:type/:name/associations', (schema, req) => {
      // list query param not required for this endpoint
      assert.deepEqual(
        req.params,
        this.params[handler.numberOfCalls - 1],
        'request is made to correct endpoint when querying'
      );
      return associationsResponse(schema, req);
    });

    const spy = sinon.spy(this.store.serializerFor('sync/association'), 'normalizeFetchByDestinations');
    await this.store.adapterFor('sync/association').fetchByDestinations(this.params);
    assert.true(spy.calledTwice, 'Serializer method used on each response');
  });
});
