import http from "k6/http";
import { check, sleep } from "k6";
import { Counter, Trend } from "k6/metrics";
import exec from "k6/execution";

const baseUrl = (__ENV.BASE_URL || "http://localhost:3000").replace(/\/+$/, "");
const username = __ENV.USERNAME || "alice";
const password = __ENV.PASSWORD || "alice123";

const bulkVUs = Number(__ENV.BULK_VUS || 20);
const bulkRounds = Number(__ENV.BULK_ROUNDS || 3);
const smallVUs = Number(__ENV.SMALL_VUS || 6);
const smallIterationsPerVu = Number(__ENV.SMALL_ITERATIONS_PER_VU || 12);
const crashDelaySeconds = Number(__ENV.CRASH_DELAY_SECONDS || 10);
const crashSleeperVus = 1;
const pauseBetweenRequestsMs = Number(__ENV.PAUSE_BETWEEN_REQUESTS_MS || 0);

const uploadLatency = new Trend("upload_latency_ms");
const uploadErrors = new Counter("upload_errors");

const filePaths = JSON.parse(__ENV.K6_FILE_LIST_JSON || "[]");
if (!filePaths.length) {
  throw new Error("K6_FILE_LIST_JSON is empty. Provide testData file list via environment.");
}

const files = filePaths.map((path) => {
  const data = open(path, "b");
  const filename = path.split("/").pop() || "upload.bin";
  return { path, data, filename, size: data.byteLength || 0 };
});

const sortedBySize = [...files].sort((a, b) => a.size - b.size);
const smallFileCount = Math.max(1, Math.min(Number(__ENV.SMALL_FILE_COUNT || 5), sortedBySize.length));
const smallFiles = sortedBySize.slice(0, smallFileCount);

const bulkTotalIterations = files.length * bulkRounds;

export const options = {
  thresholds: {
    http_req_failed: ["rate<0.1"],
    upload_errors: ["count<1000"]
  },
  scenarios: {
    bulk_upload_all_content: {
      executor: "shared-iterations",
      vus: bulkVUs,
      iterations: bulkTotalIterations,
      exec: "bulkUpload"
    },
    bulk_upload_all_content_2: {
      executor: "shared-iterations",
      vus: bulkVUs,
      iterations: bulkTotalIterations,
      exec: "bulkUpload"
    },
    smaller_upload_during_crash: {
      executor: "per-vu-iterations",
      startTime: "5s",
      vus: smallVUs,
      iterations: smallIterationsPerVu,
      exec: "smallerUpload"
    },
    kill_one_instance_mid_test: {
      executor: "per-vu-iterations",
      startTime: `${5 + crashDelaySeconds}s`,
      vus: crashSleeperVus,
      iterations: 1,
      exec: "triggerCrash"
    }
  }
};

export function setup() {
  const loginRes = http.post(
    `${baseUrl}/api/auth/login`,
    JSON.stringify({ username, password }),
    { headers: { "Content-Type": "application/json" } }
  );
  check(loginRes, { "login success": (r) => r.status === 200 });
  if (loginRes.status !== 200) {
    throw new Error(`Login failed: status=${loginRes.status} body=${loginRes.body}`);
  }

  return { token: loginRes.json("token") };
}

function uploadOne(token, file, tagPrefix) {
  const payload = {
    file: http.file(file.data, file.filename),
    filename: file.filename,
    tags: `${tagPrefix},k6`,
    folderPath: `/k6/${tagPrefix}`
  };

  const res = http.post(`${baseUrl}/api/files`, payload, {
    headers: { Authorization: `Bearer ${token}` }
  });

  uploadLatency.add(res.timings.duration);
  const ok = check(res, { "upload status 201": (r) => r.status === 201 });
  if (!ok) {
    uploadErrors.add(1);
  }
  if (pauseBetweenRequestsMs > 0) {
    sleep(pauseBetweenRequestsMs / 1000);
  }
}

export function bulkUpload(data) {
  const index = exec.scenario.iterationInTest % files.length;
  uploadOne(data.token, files[index], "bulk");
}

export function smallerUpload(data) {
  const index = exec.vu.iterationInInstance % smallFiles.length;
  uploadOne(data.token, smallFiles[index], "small");
}

export function triggerCrash() {
  const crashRes = http.post(`${baseUrl}/api/crash`, null, {
    headers: { "Content-Type": "application/json" }
  });
  check(crashRes, { "crash endpoint accepted": (r) => r.status === 202 || r.status === 500 || r.status === 0 });
}
