import http from 'k6/http'
import { check, sleep } from 'k6'
import { Rate, Trend } from 'k6/metrics'

const BASE_URL = (__ENV.NEXUS_BASE_URL || 'http://127.0.0.1:18880').replace(/\/+$/, '')
const BEARER_TOKEN = (__ENV.NEXUS_BEARER_TOKEN || '').trim()
const API_KEY = (__ENV.NEXUS_API_KEY || '').trim()
const IS_SMOKE = (__ENV.NEXUS_K6_SMOKE || '').trim() === '1'
const TARGET = new URL(BASE_URL)
const IS_LOOPBACK = ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(TARGET.hostname)
const INSECURE_TLS = (__ENV.NEXUS_INSECURE_TLS || '').trim() === '1'
const ALLOW_REMOTE = (__ENV.NEXUS_K6_ALLOW_REMOTE || '').trim() === '1'
const REQUIRE_DONE_CURSOR = (__ENV.NEXUS_K6_REQUIRE_DONE_CURSOR || '').trim()
  ? (__ENV.NEXUS_K6_REQUIRE_DONE_CURSOR || '').trim() === '1'
  : !IS_SMOKE

if (!BEARER_TOKEN && !API_KEY) {
  throw new Error(
    'Set NEXUS_BEARER_TOKEN or NEXUS_API_KEY. Credentials are read from the environment and are never printed.',
  )
}
if (!['http:', 'https:'].includes(TARGET.protocol)) {
  throw new Error('NEXUS_BASE_URL must use HTTP or HTTPS.')
}
if (
  TARGET.username
  || TARGET.password
  || TARGET.search
  || TARGET.hash
  || !['', '/'].includes(TARGET.pathname)
) {
  throw new Error(
    'NEXUS_BASE_URL must be an origin without embedded credentials, path, query, or fragment.',
  )
}
if (!IS_LOOPBACK && !ALLOW_REMOTE) {
  throw new Error(
    'Remote k6 targets are blocked. Set NEXUS_K6_ALLOW_REMOTE=1 only for an isolated non-production environment.',
  )
}
if (!IS_LOOPBACK && TARGET.protocol !== 'https:') {
  throw new Error(
    'Remote k6 targets must use HTTPS because the test sends an authenticated credential.',
  )
}
if (!IS_LOOPBACK && INSECURE_TLS) {
  throw new Error('NEXUS_INSECURE_TLS is allowed only for loopback diagnostics.')
}

const boardRequestDuration = new Trend('nexus_board_request_duration', true)
const boardInitialDuration = new Trend('nexus_board_initial_duration', true)
const boardDoneDuration = new Trend('nexus_board_done_duration', true)
const boardFailures = new Rate('nexus_board_failures')
const boardContractFailures = new Rate('nexus_board_contract_failures')
const boardDatasetFailures = new Rate('nexus_board_dataset_failures')

export const options = {
  scenarios: {
    task_board: IS_SMOKE
      ? {
          executor: 'constant-vus',
          vus: 1,
          duration: '10s',
          gracefulStop: '5s',
        }
      : {
          executor: 'constant-vus',
          vus: 10,
          duration: '2m',
          gracefulStop: '15s',
        },
  },
  thresholds: IS_SMOKE
    ? {
        nexus_board_failures: ['rate<0.05'],
        nexus_board_contract_failures: ['rate<0.05'],
        nexus_board_dataset_failures: ['rate<0.05'],
      }
    : {
        'http_req_duration{endpoint:task-board,page:initial}': ['p(95)<500'],
        'http_req_duration{endpoint:task-board,page:done}': ['p(95)<300'],
        'http_req_failed{endpoint:task-board,page:initial}': ['rate<0.01'],
        'http_req_failed{endpoint:task-board,page:done}': ['rate<0.01'],
        nexus_board_initial_duration: ['p(95)<500'],
        nexus_board_done_duration: ['p(95)<300'],
        nexus_board_failures: ['rate<0.01'],
        nexus_board_contract_failures: ['rate<0.01'],
        nexus_board_dataset_failures: ['rate<0.01'],
        checks: ['rate>0.99'],
      },
  userAgent: 'nexus-task-board-k6/1.0',
  insecureSkipTLSVerify: INSECURE_TLS,
  noConnectionReuse: false,
}

function authHeaders() {
  const headers = {
    Accept: 'application/json',
  }

  if (BEARER_TOKEN) headers.Authorization = `Bearer ${BEARER_TOKEN}`
  if (API_KEY) headers['X-Nexus-Api-Key'] = API_KEY
  return headers
}

function readBoardPage(url, page) {
  const response = http.get(url, {
    headers: authHeaders(),
    tags: {
      endpoint: 'task-board',
      page,
    },
    timeout: '10s',
  })

  boardRequestDuration.add(response.timings.duration, { page })
  if (page === 'initial') boardInitialDuration.add(response.timings.duration)
  if (page === 'done') boardDoneDuration.add(response.timings.duration)
  const httpOk = response.status === 200
  boardFailures.add(!httpOk, { page })

  let body = null
  if (httpOk) {
    try {
      body = response.json()
    } catch {
      body = null
    }
  }

  const contractOk = Boolean(
    body
      && typeof body.revision === 'string'
      && Array.isArray(body.offen)
      && Array.isArray(body.inProgress)
      && Array.isArray(body.review)
      && Array.isArray(body.blocked)
      && Array.isArray(body.done)
      && typeof body.hasMoreDone === 'boolean',
  )
  boardContractFailures.add(!contractOk, { page })

  check(response, {
    [`${page}: returns 200`]: () => httpOk,
    [`${page}: preserves the board contract`]: () => contractOk,
    [`${page}: emits Server-Timing`]: res => Boolean(res.headers['Server-Timing']),
  })

  return contractOk ? body : null
}

export default function () {
  const firstPage = readBoardPage(
    `${BASE_URL}/api/v1/tasks/board?doneLimit=50`,
    'initial',
  )

  const hasDoneCursor = Boolean(firstPage?.hasMoreDone && firstPage.nextDoneCursor)
  const datasetOk = !REQUIRE_DONE_CURSOR || hasDoneCursor
  boardDatasetFailures.add(!datasetOk)
  check(firstPage, {
    'acceptance dataset exposes a Done continuation cursor': () => datasetOk,
  })

  if (hasDoneCursor) {
    readBoardPage(
      `${BASE_URL}/api/v1/tasks/board?doneLimit=50&doneCursor=${encodeURIComponent(firstPage.nextDoneCursor)}`,
      'done',
    )
  }

  sleep(0.75 + Math.random() * 0.5)
}
