/**
 * SCENARIO: pairing-flow.js
 * Guardian invite → User accept/reject → Pairing status check → Unpair
 *
 * Tujuan: Test business logic pairing — paling complex di WalkGuide karena
 * melibatkan dua role berbeda, constraint unique, dan state transitions.
 * Target: p95 pairing latency < 600ms, error rate < 1%
 */
import { sleep, check } from "k6";
import {
  DEFAULT_THRESHOLDS,
  handleSummary,
} from "../modules/metrics-helper.js";
import * as api from "../modules/api-client.js";
import { testData } from "../modules/test-data.js";

export const options = {
  scenarios: {
    pairing_flow: {
      executor: "ramping-vus",
      startVUs: 0,
      stages: [
        { duration: "20s", target: 5 },
        { duration: "60s", target: 20 },
        { duration: "60s", target: 20 },
        { duration: "20s", target: 0 },
      ],
      gracefulRampDown: "20s",
    },
  },
  thresholds: {
    ...DEFAULT_THRESHOLDS,
    walkguide_pairing_latency_ms: ["p(95)<600", "p(99)<1200"],
  },
};

// ── Helper: register dan dapat tokens ────────────────────────────────────────
function registerGetTokens(payload) {
  const res = api.register(payload);
  if (res.status !== 200) return null;
  const body = api.parseBody(res);
  return body && body.data ? body.data : null;
}

// ── Main VU function ──────────────────────────────────────────────────────────
export default function pairingFlow() {
  const vuId = __VU;
  const iter = __ITER;
  const suffix = `${vuId}_${iter}_${Date.now()}`;

  // ── Step 1: Register Guardian ─────────────────────────────────────────────
  const gPayload = testData.guardianRegisterPayload(suffix);
  const gTokens = registerGetTokens(gPayload);

  if (!gTokens) {
    console.error(`[pairing-flow] Guardian register failed for VU ${vuId}`);
    sleep(1);
    return;
  }

  sleep(0.3);

  // ── Step 2: Register User (tunanetra) ─────────────────────────────────────
  const uPayload = testData.userRegisterPayload(suffix);
  const uTokens = registerGetTokens(uPayload);

  if (!uTokens) {
    console.error(`[pairing-flow] User register failed for VU ${vuId}`);
    sleep(1);
    return;
  }

  // uniqueUserId harus ada untuk user baru
  const uniqueUserId = uTokens.uniqueUserId;
  if (!uniqueUserId) {
    console.warn(`[pairing-flow] uniqueUserId missing, VU ${vuId}`);
  }

  sleep(0.3);

  // ── Step 3: Check pairing status (harus NONE/belum paired) ───────────────
  const statusRes1 = api.getPairingStatus(gTokens.accessToken);
  check(statusRes1, {
    "pairing-status initial: 2xx": (r) => r.status >= 200 && r.status < 300,
  });

  sleep(0.2);

  // ── Step 4: Guardian invite User ─────────────────────────────────────────
  const inviteRes = api.inviteUser(
    gTokens.accessToken,
    uniqueUserId || testData.fakeUniqueUserId(),
  );
  check(inviteRes, {
    "pairing-invite: status 2xx": (r) => r.status >= 200 && r.status < 300,
    "pairing-invite: has data": (r) => {
      const b = api.parseBody(r);
      return b && b.data !== undefined;
    },
  });

  let pairingId = null;
  if (inviteRes.status >= 200 && inviteRes.status < 300) {
    const body = api.parseBody(inviteRes);
    pairingId = body && body.data ? body.data.pairingId : null;
  }

  sleep(0.3);

  // ── Step 5: User check pending invitation ─────────────────────────────────
  const statusRes2 = api.getPairingStatus(uTokens.accessToken);
  check(statusRes2, {
    "user-pairing-status: 2xx": (r) => r.status >= 200 && r.status < 300,
    "user-pairing-status: PENDING": (r) => {
      const b = api.parseBody(r);
      // Bisa PENDING atau NONE jika invite belum proses
      return b && b.data !== undefined;
    },
  });

  sleep(0.2);

  // ── Step 6: User accept atau reject (alternating per iteration) ───────────
  if (pairingId) {
    const shouldAccept = iter % 3 !== 0; // 2/3 accept, 1/3 reject
    const respondRes = api.respondPairing(
      uTokens.accessToken,
      pairingId,
      shouldAccept,
    );
    check(respondRes, {
      "pairing-respond: status 2xx": (r) => r.status >= 200 && r.status < 300,
    });

    sleep(0.3);

    // ── Step 7: Confirm status setelah respond ──────────────────────────────
    const statusRes3 = api.getPairingStatus(gTokens.accessToken);
    check(statusRes3, {
      "pairing-confirm: 2xx": (r) => r.status >= 200 && r.status < 300,
      "pairing-confirm: status matches": (r) => {
        const b = api.parseBody(r);
        if (!b || !b.data) return true; // optional
        const s = b.data.status;
        return shouldAccept ? s === "ACTIVE" : s === "REJECTED" || s === "NONE";
      },
    });

    sleep(0.3);

    // ── Step 8: Unpair (cleanup + test unpair endpoint) ────────────────────
    if (shouldAccept) {
      const unpairRes = api.unpair(gTokens.accessToken);
      check(unpairRes, {
        "unpair: status 2xx": (r) => r.status >= 200 && r.status < 300,
      });
    }
  }

  // ── Step 9: Test bahwa Guardian tidak bisa masuk ke /user endpoint ─────────
  const forbiddenRes = api.getUserProfile(gTokens.accessToken);
  check(forbiddenRes, {
    "RBAC: guardian cannot access /user/profile": (r) =>
      r.status === 403 || r.status === 401,
  });

  // ── Step 10: Test bahwa User tidak bisa akses /guardian endpoint ───────────
  const forbiddenRes2 = api.getGuardianDashboard(uTokens.accessToken);
  check(forbiddenRes2, {
    "RBAC: user cannot access /guardian/dashboard": (r) =>
      r.status === 403 || r.status === 401,
  });

  sleep(testData.randomFloat(0.5, 1.5, 1));
}

export function setup() {
  const res = api.ping();
  if (res.status !== 200)
    throw new Error(`Backend not reachable: ${res.status}`);
  console.log("✅ Pairing flow test starting.");
}

export { handleSummary };
