'use strict';

const assert = require('assert');
const fs = require('fs');
const services = [
  ['aisfor', 'E:\\aisfor\\services\\promotedActiveGate', 'E:\\aisfor\\server.js', true, false],
  ['aistalk', 'F:\\aistalk\\services\\promotedActiveGate', 'F:\\aistalk\\server.js', true, true],
  ['aisfor-group', 'E:\\aisfor-group\\services\\promotedActiveGate', 'E:\\aisfor-group\\app.js', false, true],
  ['aismall', 'E:\\aismall\\services\\promotedActiveGate', 'E:\\aismall\\app.js', true, true],
  ['plai-sharing', 'F:\\plai-sharing\\services\\promotedActiveGate', 'F:\\plai-sharing\\server.js', true, false]
];

function responseProbe() {
  return {
    headers: {},
    setHeader(name, value) { this.headers[name] = value; },
    end(value) { this.body = JSON.parse(value); }
  };
}

function mongoMock(options = {}) {
  const state = {
    leaseOwner: options.leaseOwner || '',
    leaseHolder: options.leaseHolder || '',
    fencingToken: 0,
    renewalMatches: options.renewalMatches !== false,
    majorityProbeFails: options.majorityProbeFails === true
  };
  const hello = {
    ...(options.setName === null ? {} : { setName: options.setName || 'plai-rs' }),
    isWritablePrimary: options.isWritablePrimary !== false,
    hosts: Array.from({ length: options.hostCount ?? 3 }, (_, index) => `mongo-${index}:27017`),
    ...(options.logicalSessions === false ? {} : { logicalSessionTimeoutMinutes: 30 })
  };
  const db = {
    admin: () => ({ command: async () => hello }),
    collection(name) {
      if (name === '__plai_promotion_probes') {
        return {
          async insertOne() {
            if (state.majorityProbeFails) throw new Error('synthetic majority failure');
            return { acknowledged: true };
          },
          async deleteOne() { return { deletedCount: 1 }; }
        };
      }
      if (name === '__plai_promotion_leases') {
        return {
          async findOneAndUpdate(_filter, update) {
            const requestedOwner = update.$set.ownerId;
            const requestedHolder = update.$set.holderId;
            if (state.leaseHolder) {
              const error = new Error('duplicate key');
              error.code = 11000;
              throw error;
            }
            state.leaseOwner = requestedOwner;
            state.leaseHolder = requestedHolder;
            state.fencingToken += 1;
            return {
              _id: update.$set.serviceId,
              ownerId: requestedOwner,
              holderId: requestedHolder,
              fencingToken: state.fencingToken,
              expiresAt: update.$set.expiresAt
            };
          },
          async updateOne() {
            return { matchedCount: state.renewalMatches ? 1 : 0 };
          }
        };
      }
      throw new Error(`unexpected collection: ${name}`);
    }
  };
  return {
    mongoose: { connection: { readyState: 1, db } },
    state
  };
}

function socketServerProbe() {
  return {
    middleware: null,
    disconnected: 0,
    use(fn) { this.middleware = fn; },
    disconnectSockets() { this.disconnected += 1; }
  };
}

async function rejectsCode(fn, code) {
  await assert.rejects(fn, error => String(error?.message || '').includes(code));
}

(async () => {
  const results = [];
  for (const [serviceId, modulePath, entryPath, usesSocketIo, requiresIndependentSessionProbe] of services) {
    const { createPromotedActiveGate } = require(modulePath);
    const normal = createPromotedActiveGate({ serviceId, env: {} });
    assert.equal(normal.enabled, false);
    let normalNext = false;
    normal.middleware({}, {}, () => { normalNext = true; });
    assert.equal(normalNext, true);
    assert.equal(normal.installSocketGate(null), false);

    const missingId = createPromotedActiveGate({
      serviceId,
      env: { PLAI_SERVICE_MODE: 'PROMOTED_ACTIVE' },
      onLeaseLost() {}
    });
    await rejectsCode(() => missingId.verifyAndOpen({ mongoose: mongoMock().mongoose }),
      'PROMOTION_REPLICA_ID_REQUIRED');

    let leaseLossEvents = 0;
    const gate = createPromotedActiveGate({
      serviceId,
      env: {
        PLAI_SERVICE_MODE: 'PROMOTED_ACTIVE',
        PLAI_REPLICA_ID: `replica-${serviceId}`,
        PLAI_PROMOTION_LEASE_TTL_MS: '15000'
      },
      onLeaseLost() { leaseLossEvents += 1; }
    });
    const closed = responseProbe();
    gate.middleware({ path: '/dynamic' }, closed, () => assert.fail('closed promotion gate passed traffic'));
    assert.equal(closed.statusCode, 503);
    assert.equal(closed.body.code, 'PROMOTED_ACTIVE_DATA_PLANE_NOT_READY');

    const socketServer = socketServerProbe();
    assert.equal(gate.installSocketGate(socketServer), true);
    let socketClosedError = null;
    socketServer.middleware({}, error => { socketClosedError = error; });
    assert.equal(socketClosedError?.message, 'PROMOTED_ACTIVE_DATA_PLANE_NOT_READY');

    await rejectsCode(() => gate.verifyAndOpen({ mongoose: null }), 'MONGO_CONNECTION_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ setName: null }).mongoose,
      sharedSessionStoreReady: true
    }), 'MONGO_REPLICA_SET_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ isWritablePrimary: false }).mongoose,
      sharedSessionStoreReady: true
    }), 'MONGO_WRITABLE_PRIMARY_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ hostCount: 2 }).mongoose,
      sharedSessionStoreReady: true
    }), 'MONGO_THREE_DATA_MEMBERS_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ logicalSessions: false }).mongoose,
      sharedSessionStoreReady: true
    }), 'MONGO_LOGICAL_SESSIONS_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock().mongoose,
      sharedSessionStoreReady: false
    }), 'SHARED_SESSION_STORE_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock().mongoose,
      sharedSessionStoreReady: true,
      requireSocketAdapter: true
    }), 'SOCKET_CLUSTER_ADAPTER_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ majorityProbeFails: true }).mongoose,
      sharedSessionStoreReady: true,
      socketAdapter: { enabled: true },
      requireSocketAdapter: true
    }), 'MONGO_MAJORITY_WRITE_REQUIRED');
    await rejectsCode(() => gate.verifyAndOpen({
      mongoose: mongoMock({ leaseOwner: 'another-replica', leaseHolder: 'another-process' }).mongoose,
      sharedSessionStoreReady: true,
      socketAdapter: { enabled: true },
      requireSocketAdapter: true
    }), 'PROMOTION_LEASE_HELD_BY_ANOTHER_REPLICA');

    const liveMongo = mongoMock();
    const opened = await gate.verifyAndOpen({
      mongoose: liveMongo.mongoose,
      sharedSessionStoreReady: true,
      socketAdapter: { enabled: true },
      requireSocketAdapter: true
    });
    assert.equal(opened.ready, true);
    assert.equal(opened.fencingToken, 1);
    let openedNext = false;
    gate.middleware({ path: '/dynamic' }, {}, () => { openedNext = true; });
    assert.equal(openedNext, true);
    let socketOpened = false;
    socketServer.middleware({}, error => {
      assert.equal(error, undefined);
      socketOpened = true;
    });
    assert.equal(socketOpened, true);

    liveMongo.state.renewalMatches = false;
    await rejectsCode(() => gate.renewNow(), 'PROMOTION_LEASE_LOST');
    assert.equal(gate.ready, false);
    assert.equal(leaseLossEvents, 1);
    assert.equal(socketServer.disconnected, 1);
    gate.stop();

    const entrySource = fs.readFileSync(entryPath, 'utf8');
    assert.equal(entrySource.includes('promotedActiveGate.installSocketGate'), usesSocketIo);
    if (requiresIndependentSessionProbe) {
      assert(entrySource.includes('await sharedSessionStore.collectionP') || entrySource.includes('await store.collectionP'));
      assert(!entrySource.includes('sharedSessionStoreReady: true'));
    }
    results.push({
      serviceId,
      defaultPrimaryUnchanged: true,
      replicaIdentityRequired: true,
      closedBeforeVerification: true,
      standaloneMongoRejected: true,
      nonPrimaryMongoRejected: true,
      fewerThanThreeMongoMembersRejected: true,
      logicalSessionsRequired: true,
      missingSharedSessionRejected: true,
      missingSocketAdapterRejected: true,
      majorityWriteProbeRequired: true,
      competingPromotionLeaseRejected: true,
      fencingTokenIssued: true,
      leaseLossClosedHttpGate: true,
      leaseLossDisconnectedSockets: usesSocketIo,
      leaseLossFailStopTriggered: true,
      independentSessionStoreProbed: requiresIndependentSessionProbe
    });
  }
  console.log(JSON.stringify({
    ok: true,
    schemaVersion: 'plai-promoted-active-gate-self-test/v2',
    results
  }, null, 2));
})().catch(error => {
  console.error(error);
  process.exit(1);
});
