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

const BASE_URL = (__ENV.BASE_URL || 'https://stend.4mont.ru').replace(/\/$/, '');
const HOST_HEADER = (__ENV.HOST_HEADER || '').trim();
const SERVICE_SLUGS = (__ENV.SERVICE_SLUGS || 'vmmanager')
  .split(',')
  .map((s) => s.trim())
  .filter(Boolean);
const HEARTBEATS = Number(__ENV.HEARTBEATS || 3);
const THINK_TIME = Number(__ENV.THINK_TIME || 1);
const CLOSE_SESSION = (__ENV.CLOSE_SESSION || '1') !== '0';
const PROFILE = (__ENV.PROFILE || 'smoke').toLowerCase();

const usersFromCsv = parseUsersCsv(__ENV.USERS_CSV || '');
const singleUser = {
  username: __ENV.USERNAME || '',
  password: __ENV.PASSWORD || '',
};

const openAttempts = new Counter('open_attempts');
const openSuccess = new Counter('open_success');
const openRejected = new Counter('open_rejected');
const limitRedirects = new Counter('limit_redirects');
const touchRejected = new Counter('touch_rejected');
const closeCalls = new Counter('close_calls');
const loginFailures = new Counter('login_failures');
const flowErrors = new Rate('flow_errors');
const openLatency = new Trend('open_latency_ms');

const PROFILE_OPTIONS = {
  smoke: {
    vus: 5,
    duration: '1m',
  },
  load: {
    vus: 30,
    duration: '10m',
  },
  stress: {
    stages: [
      { duration: '2m', target: 30 },
      { duration: '5m', target: 80 },
      { duration: '2m', target: 0 },
    ],
  },
};

const selected = PROFILE_OPTIONS[PROFILE] || PROFILE_OPTIONS.smoke;

export const options = {
  ...selected,
  noCookiesReset: true,
  insecureSkipTLSVerify: (__ENV.INSECURE_TLS || '0') === '1',
  thresholds: {
    http_req_failed: ['rate<0.02'],
    http_req_duration: ['p(95)<2000'],
    flow_errors: ['rate<0.05'],
  },
};

let loggedInKey = '';

function withHostHeaders(headers = {}) {
  if (!HOST_HEADER) return headers;
  return { ...headers, Host: HOST_HEADER };
}

function parseUsersCsv(raw) {
  if (!raw.trim()) return [];
  return raw
    .split(';')
    .map((pair) => pair.trim())
    .filter(Boolean)
    .map((pair) => {
      const idx = pair.indexOf(':');
      if (idx <= 0) return null;
      return {
        username: pair.slice(0, idx).trim(),
        password: pair.slice(idx + 1).trim(),
      };
    })
    .filter((u) => u && u.username && u.password);
}

function currentCredentials() {
  if (usersFromCsv.length > 0) {
    const vu = exec.vu.idInTest || 1;
    return usersFromCsv[(vu - 1) % usersFromCsv.length];
  }
  return singleUser;
}

function ensureLoggedIn() {
  const creds = currentCredentials();
  if (!creds.username || !creds.password) {
    fail('Set USERNAME/PASSWORD or USERS_CSV (username:password;username2:password2)');
  }

  const userKey = `${creds.username}:${creds.password}`;
  if (loggedInKey === userKey) return creds;

  const jar = http.cookieJar();
  jar.clear(BASE_URL);

  const landing = http.get(`${BASE_URL}/`, {
    redirects: 0,
    tags: { name: 'GET /' },
    headers: withHostHeaders(),
  });
  const csrfFromCookie = (jar.cookiesForURL(BASE_URL).csrf_token || [])[0] || '';
  const csrf = csrfFromCookie || ((landing.cookies.csrf_token || [])[0] || {}).value || '';
  if (!csrf) {
    loginFailures.add(1);
    fail('CSRF cookie not found on GET /');
  }

  const payload = [
    `username=${encodeURIComponent(creds.username)}`,
    `password=${encodeURIComponent(creds.password)}`,
    `csrf_token=${encodeURIComponent(csrf)}`,
  ].join('&');

  const loginRes = http.post(`${BASE_URL}/login`, payload, {
    redirects: 0,
    headers: withHostHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }),
    tags: { name: 'POST /login' },
  });

  const ok = check(loginRes, {
    'login status is redirect': (r) => r.status === 303,
  });

  const hasAuth = (jar.cookiesForURL(BASE_URL).portal_auth || []).length > 0;
  if (!ok || !hasAuth) {
    loginFailures.add(1);
    fail(`Login failed for ${creds.username}: status=${loginRes.status}`);
  }

  loggedInKey = userKey;
  return creds;
}

function pickSlug() {
  return SERVICE_SLUGS[Math.floor(Math.random() * SERVICE_SLUGS.length)];
}

function extractSessionId(location) {
  const match = (location || '').match(/\/s\/([0-9a-fA-F-]{36})\//);
  return match ? match[1] : '';
}

export default function () {
  ensureLoggedIn();

  const slug = pickSlug();
  openAttempts.add(1);

  const openRes = http.get(`${BASE_URL}/go/${slug}`, {
    redirects: 0,
    tags: { name: 'GET /go/:slug' },
    headers: withHostHeaders(),
  });
  openLatency.add(openRes.timings.duration);

  const location = openRes.headers.Location || '';
  const sessionId = extractSessionId(location);

  const opened = check(openRes, {
    'open returns redirect': (r) => r.status === 303,
  });

  if (!opened) {
    openRejected.add(1);
    flowErrors.add(1);
    sleep(THINK_TIME);
    return;
  }

  if (location.includes('launch_error=max_services')) {
    limitRedirects.add(1);
    flowErrors.add(1);
    sleep(THINK_TIME);
    return;
  }

  if (!sessionId) {
    openRejected.add(1);
    flowErrors.add(1);
    sleep(THINK_TIME);
    return;
  }

  openSuccess.add(1);

  for (let i = 0; i < HEARTBEATS; i += 1) {
    const touchRes = http.post(`${BASE_URL}/api/sessions/${sessionId}/touch`, null, {
      redirects: 0,
      tags: { name: 'POST /api/sessions/:id/touch' },
      headers: withHostHeaders(),
    });

    if (touchRes.status === 410) {
      touchRejected.add(1);
      break;
    }

    const touchOk = check(touchRes, {
      'touch status 200': (r) => r.status === 200,
    });
    if (!touchOk) {
      flowErrors.add(1);
      break;
    }
    sleep(0.5);
  }

  if (CLOSE_SESSION) {
    const closeRes = http.post(`${BASE_URL}/api/sessions/${sessionId}/close`, null, {
      redirects: 0,
      tags: { name: 'POST /api/sessions/:id/close' },
      headers: withHostHeaders(),
    });
    closeCalls.add(1);
    check(closeRes, {
      'close status 200': (r) => r.status === 200,
    });
  }

  flowErrors.add(0);
  sleep(THINK_TIME);
}
