import { fileURLToPath, pathToFileURL } from 'node:url';

const DEFAULT_TIMEOUT_MS = 10_000;

function envString(env, key, fallback = '') {
  return typeof env[key] === 'string' && env[key].trim() ? env[key].trim() : fallback;
}

function envBoolean(env, key, fallback = false) {
  const value = envString(env, key).toLowerCase();
  if (!value) return fallback;
  return ['1', 'true', 'yes', 'on'].includes(value);
}

function envNumber(env, key, fallback) {
  const value = Number(envString(env, key));
  return Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback;
}

function normalizeBaseUrl(value) {
  return value.replace(/\/+$/, '');
}

function redactToken(value) {
  if (!value) return '<empty>';
  if (value.length <= 16) return '<redacted>';
  return `${value.slice(0, 6)}...${value.slice(-6)}`;
}

function buildConfig(env = process.env) {
  const apiBaseUrl = envString(env, 'AUTH_SMOKE_API_BASE_URL', envString(env, 'API_BASE', ''));
  const tenantId = envString(env, 'AUTH_SMOKE_TENANT_ID', envString(env, 'TENANT_ID', ''));
  const studentToken = envString(env, 'AUTH_SMOKE_STUDENT_ACCESS_TOKEN', envString(env, 'SUPABASE_ACCESS_TOKEN', ''));
  const tenantAdminToken = envString(env, 'AUTH_SMOKE_TENANT_ADMIN_ACCESS_TOKEN');
  const platformAdminToken = envString(env, 'AUTH_SMOKE_PLATFORM_ADMIN_ACCESS_TOKEN');
  const requireAdminTokens = envBoolean(env, 'AUTH_SMOKE_REQUIRE_ADMIN_TOKENS', false);

  const missing = [];
  if (!apiBaseUrl) missing.push('AUTH_SMOKE_API_BASE_URL');
  if (!tenantId) missing.push('AUTH_SMOKE_TENANT_ID');
  if (!studentToken) missing.push('AUTH_SMOKE_STUDENT_ACCESS_TOKEN');
  if (requireAdminTokens && !tenantAdminToken) missing.push('AUTH_SMOKE_TENANT_ADMIN_ACCESS_TOKEN');
  if (requireAdminTokens && !platformAdminToken) missing.push('AUTH_SMOKE_PLATFORM_ADMIN_ACCESS_TOKEN');

  if (missing.length > 0) {
    throw new Error(`Missing required remote auth smoke env: ${missing.join(', ')}`);
  }

  return {
    apiBaseUrl: normalizeBaseUrl(apiBaseUrl),
    tenantId,
    wrongTenantId: envString(env, 'AUTH_SMOKE_WRONG_TENANT_ID'),
    timeoutMs: envNumber(env, 'AUTH_SMOKE_TIMEOUT_MS', DEFAULT_TIMEOUT_MS),
    requireAdminTokens,
    student: {
      token: studentToken,
      expectedUserId: envString(env, 'AUTH_SMOKE_EXPECTED_STUDENT_USER_ID'),
    },
    tenantAdmin: tenantAdminToken
      ? {
          token: tenantAdminToken,
          expectedUserId: envString(env, 'AUTH_SMOKE_EXPECTED_TENANT_ADMIN_USER_ID'),
        }
      : null,
    platformAdmin: platformAdminToken
      ? {
          token: platformAdminToken,
          expectedUserId: envString(env, 'AUTH_SMOKE_EXPECTED_PLATFORM_ADMIN_USER_ID'),
          tenantId: envString(env, 'AUTH_SMOKE_PLATFORM_TENANT_ID', tenantId),
        }
      : null,
  };
}

function authHeaders(token, tenantId) {
  return {
    authorization: `Bearer ${token}`,
    ...(tenantId ? { 'x-tenant-id': tenantId } : {}),
  };
}

async function requestJson(config, path, { token, tenantId = config.tenantId, method = 'GET' } = {}) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
  const url = new URL(path, config.apiBaseUrl);
  try {
    const response = await fetch(url, {
      method,
      headers: {
        accept: 'application/json',
        ...(token ? authHeaders(token, tenantId) : {}),
      },
      signal: controller.signal,
    });
    const text = await response.text();
    let payload = {};
    if (text.trim()) {
      try {
        payload = JSON.parse(text);
      } catch {
        payload = { raw: text.slice(0, 500) };
      }
    }
    return { status: response.status, ok: response.ok, payload };
  } finally {
    clearTimeout(timeout);
  }
}

function resultCollector({ quiet = false } = {}) {
  const results = [];
  function pass(name, detail = {}) {
    results.push({ status: 'pass', name, detail });
    if (!quiet) console.log(`PASS ${name}`);
  }
  function fail(name, message, detail = {}) {
    results.push({ status: 'fail', name, message, detail });
    if (!quiet) {
      console.error(`FAIL ${name}: ${message}`);
      if (Object.keys(detail).length > 0) console.error(JSON.stringify(detail, null, 2));
    }
  }
  return { results, pass, fail };
}

function expectStatus(collector, name, response, expectedStatuses) {
  const expected = Array.isArray(expectedStatuses) ? expectedStatuses : [expectedStatuses];
  if (expected.includes(response.status)) {
    collector.pass(name, { status: response.status });
    return true;
  }
  collector.fail(name, `Expected HTTP ${expected.join('/')} but got ${response.status}`, { payload: response.payload });
  return false;
}

function expectSupabaseJwtSession(collector, name, response, expectedUserId = '') {
  if (!expectStatus(collector, `${name}.status`, response, 200)) return;
  const source = response.payload?.session?.source;
  if (source === 'supabase_jwt') collector.pass(`${name}.session_source`);
  else collector.fail(`${name}.session_source`, 'Expected session.source=supabase_jwt', { source });

  if (expectedUserId) {
    const actualUserId = response.payload?.user?.id || response.payload?.item?.userId;
    if (actualUserId === expectedUserId) collector.pass(`${name}.expected_user`);
    else collector.fail(`${name}.expected_user`, 'Authenticated user does not match expected id', { actualUserId, expectedUserId });
  }
}

async function runRemoteAuthSmoke(inputConfig, options = {}) {
  const config = inputConfig?.apiBaseUrl ? inputConfig : buildConfig(options.env || process.env);
  const collector = resultCollector({ quiet: options.quiet });

  if (!options.quiet) {
    console.log(`Remote auth smoke target: ${config.apiBaseUrl}`);
    console.log(`Student token: ${redactToken(config.student.token)}`);
    if (config.tenantAdmin) console.log(`Tenant admin token: ${redactToken(config.tenantAdmin.token)}`);
    if (config.platformAdmin) console.log(`Platform admin token: ${redactToken(config.platformAdmin.token)}`);
  }

  const studentMe = await requestJson(config, '/api/auth/me', {
    token: config.student.token,
    tenantId: config.tenantId,
  });
  expectSupabaseJwtSession(collector, 'student.auth_me', studentMe, config.student.expectedUserId);

  const studentProfile = await requestJson(config, '/api/profile/me', {
    token: config.student.token,
    tenantId: config.tenantId,
  });
  expectStatus(collector, 'student.profile_me', studentProfile, 200);
  if (config.student.expectedUserId && studentProfile.payload?.item?.userId !== config.student.expectedUserId) {
    collector.fail('student.profile_expected_user', 'Profile user does not match expected student id', {
      actualUserId: studentProfile.payload?.item?.userId,
      expectedUserId: config.student.expectedUserId,
    });
  } else {
    collector.pass('student.profile_expected_user');
  }

  const studentTenantAdmin = await requestJson(config, '/api/tenant-admin/overview', {
    token: config.student.token,
    tenantId: config.tenantId,
  });
  expectStatus(collector, 'student.tenant_admin_denied', studentTenantAdmin, 403);

  const studentPlatformAdmin = await requestJson(config, '/api/platform-admin/overview', {
    token: config.student.token,
    tenantId: '',
  });
  expectStatus(collector, 'student.platform_admin_denied', studentPlatformAdmin, 403);

  const badTokenProfile = await requestJson(config, '/api/profile/me', {
    token: 'invalid.jwt.token',
    tenantId: config.tenantId,
  });
  expectStatus(collector, 'invalid_token.profile_denied', badTokenProfile, 401);

  if (config.wrongTenantId) {
    const wrongTenantProfile = await requestJson(config, '/api/profile/me', {
      token: config.student.token,
      tenantId: config.wrongTenantId,
    });
    expectStatus(collector, 'student.wrong_tenant_denied', wrongTenantProfile, [401, 403, 404]);
  }

  if (config.tenantAdmin) {
    const tenantAdminMe = await requestJson(config, '/api/auth/me', {
      token: config.tenantAdmin.token,
      tenantId: config.tenantId,
    });
    expectSupabaseJwtSession(collector, 'tenant_admin.auth_me', tenantAdminMe, config.tenantAdmin.expectedUserId);

    const tenantOverview = await requestJson(config, '/api/tenant-admin/overview', {
      token: config.tenantAdmin.token,
      tenantId: config.tenantId,
    });
    expectStatus(collector, 'tenant_admin.overview', tenantOverview, 200);

    const tenantPlatformAdmin = await requestJson(config, '/api/platform-admin/overview', {
      token: config.tenantAdmin.token,
      tenantId: '',
    });
    expectStatus(collector, 'tenant_admin.platform_admin_denied', tenantPlatformAdmin, 403);
  } else {
    collector.pass('tenant_admin.optional_skipped');
  }

  if (config.platformAdmin) {
    const platformOverview = await requestJson(config, '/api/platform-admin/overview', {
      token: config.platformAdmin.token,
      tenantId: '',
    });
    expectStatus(collector, 'platform_admin.overview', platformOverview, 200);

    if (config.platformAdmin.tenantId) {
      const platformMe = await requestJson(config, '/api/auth/me', {
        token: config.platformAdmin.token,
        tenantId: config.platformAdmin.tenantId,
      });
      expectSupabaseJwtSession(collector, 'platform_admin.auth_me', platformMe, config.platformAdmin.expectedUserId);
    }
  } else {
    collector.pass('platform_admin.optional_skipped');
  }

  const failed = collector.results.filter(item => item.status === 'fail');
  if (!options.quiet) {
    console.log(`\nRemote Auth/JWKS smoke checks: ${collector.results.length - failed.length} passed, ${failed.length} failed.`);
  }
  if (failed.length > 0) {
    const error = new Error(`Remote Auth/JWKS smoke failed with ${failed.length} failing checks`);
    error.results = collector.results;
    throw error;
  }
  return collector.results;
}

async function main() {
  try {
    await runRemoteAuthSmoke();
  } catch (error) {
    console.error(error.message);
    if (/Missing required remote auth smoke env/.test(error.message)) {
      console.error(`
Required example:
  AUTH_SMOKE_API_BASE_URL=https://api.example.com
  AUTH_SMOKE_TENANT_ID=<tenant-uuid>
  AUTH_SMOKE_STUDENT_ACCESS_TOKEN=<real-supabase-access-token>

Recommended full pre-production example:
  AUTH_SMOKE_TENANT_ADMIN_ACCESS_TOKEN=<tenant-admin-supabase-access-token>
  AUTH_SMOKE_PLATFORM_ADMIN_ACCESS_TOKEN=<platform-admin-supabase-access-token>
  AUTH_SMOKE_WRONG_TENANT_ID=<another-tenant-uuid>
  AUTH_SMOKE_REQUIRE_ADMIN_TOKENS=true
`);
    }
    process.exitCode = 1;
  }
}

const currentFile = fileURLToPath(import.meta.url);
if (process.argv[1] && fileURLToPath(pathToFileURL(process.argv[1])) === currentFile) {
  await main();
}

export { buildConfig, runRemoteAuthSmoke };
