import assert from 'node:assert/strict';
import { createHmac, randomUUID } from 'node:crypto';
import { pool } from './db';
import { config } from './config';

const baseUrl = process.env.SMOKE_API_URL ?? 'http://127.0.0.1:4000/api';
export const createdUserIds: string[] = [];

export interface Session {
  token: string;
  user: { id: string; username: string };
}

export const call = async <T>(
  path: string,
  expectedStatus: number,
  token?: string,
  method = 'GET',
  body?: unknown,
): Promise<T> => {
  const response = await fetch(`${baseUrl}${path}`, {
    method,
    headers: {
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
    },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  });
  const data = await response.json() as T;
  assert.equal(response.status, expectedStatus, `${method} ${path}: ${JSON.stringify(data)}`);
  return data;
};

export const register = async (label: string): Promise<Session> => {
  const suffix = randomUUID();
  const session = await call<Session>('/v1/auth/register', 201, undefined, 'POST', {
    email: `smoke-${label}-${suffix}@example.test`,
    password: `Smoke-${suffix}-Password!`,
    displayName: `Smoke ${label}`,
  });
  createdUserIds.push(session.user.id);
  return session;
};

export const contactHashes = (fingerprintKey: string) => Array.from({ length: 7 }, (_, index) =>
  createHmac('sha256', Buffer.from(fingerprintKey, 'base64url'))
    .update(`synthetic-common-contact-${index}`).digest('hex'));

export const contactFingerprints = (fingerprintKey: string) => contactHashes(fingerprintKey)
  .map((digest, index) => ({
    digest,
    name: `Synthetic shared contact ${index + 1}`,
    phone: `+4917000000${String(index).padStart(2, '0')}`,
  }));

export const fingerprintKeyFor = async (session: Session, invitationId: string): Promise<string> => {
  const state = await call<{ guardian: { invitations: Array<{
    id: string; fingerprintKey: string | null;
  }> } }>('/v1/me/guardian-verification', 200, session.token);
  const key = state.guardian.invitations.find((item) => item.id === invitationId)?.fingerprintKey;
  assert(key);
  return key;
};

export const insertSyntheticCallSession = async (meetingId: string, userId: string): Promise<string> => {
  const sessionId = randomUUID();
  await pool.query(
    `INSERT INTO meeting_call_sessions (session_id,meeting_id,user_id,provider_joined_at)
     VALUES ($1,$2,$3,NOW())`,
    [sessionId, meetingId, userId],
  );
  return sessionId;
};

export const registerVerifiedGuardian = async (candidate: Session, label: string): Promise<Session> => {
  const superseded = await call<{ challengeId: string; payload: string }>(
    '/v1/me/guardian-qr', 201, candidate.token, 'POST',
  );
  const issuedAt = Date.now();
  const issued = await call<{ challengeId: string; payload: string; expiresAt: string }>(
    '/v1/me/guardian-qr', 201, candidate.token, 'POST',
  );
  assert.notEqual(issued.payload, superseded.payload);
  const supersededStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${superseded.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(supersededStatus.status, 'issued');
  const issuedStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${issued.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(issuedStatus.status, 'issued');
  const issuedUrl = new URL(issued.payload);
  assert.equal(issuedUrl.origin + issuedUrl.pathname, config.guardianQrEntryUrl);
  assert.match(issuedUrl.hash, /^#token=[A-Za-z0-9_-]{43}$/);
  const lifetimeSeconds = (Date.parse(issued.expiresAt) - issuedAt) / 1000;
  assert(lifetimeSeconds > 12 && lifetimeSeconds <= 16);
  const supersededUrl = new URL(superseded.payload);
  const supersededToken = new URLSearchParams(supersededUrl.hash.slice(1)).get('token');
  assert(supersededToken);
  const nativePayload = `alatalabak://guardian-login?token=${supersededToken}`;
  const claim = await call<{ kind: string; enrollmentToken: string }>(
    '/v1/guardian-qr/claim', 200, undefined, 'POST', { payload: nativePayload },
  );
  assert.equal(claim.kind, 'enroll');
  const claimedStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${superseded.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(claimedStatus.status, 'claimed');
  const rotatedStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${issued.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(rotatedStatus.status, 'expired');
  await call('/v1/me/guardian-qr', 409, candidate.token, 'POST');
  await call('/v1/guardian-qr/claim', 410, undefined, 'POST', { payload: issued.payload });
  const released = await call<{ released: boolean }>(
    '/v1/guardian-qr/release', 200, undefined, 'POST', {
      enrollmentToken: claim.enrollmentToken,
    },
  );
  assert.equal(released.released, true);
  const releasedStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${superseded.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(releasedStatus.status, 'expired');
  const replacement = await call<{ challengeId: string; payload: string }>(
    '/v1/me/guardian-qr', 201, candidate.token, 'POST',
  );
  const enrollmentClaim = await call<{ kind: string; enrollmentToken: string }>(
    '/v1/guardian-qr/claim', 200, undefined, 'POST', { payload: replacement.payload },
  );
  assert.equal(enrollmentClaim.kind, 'enroll');
  const guardian = await call<Session>(
    '/v1/guardian-qr/enroll', 201, undefined, 'POST', {
      enrollmentToken: enrollmentClaim.enrollmentToken, fullName: `Guardian ${label}`,
      phone: '+4915112345678', password: `Guardian-${label}-Password!`,
      passwordConfirmation: `Guardian-${label}-Password!`,
    },
  );
  createdUserIds.push(guardian.user.id);
  const passwordLogin = await call<Session>('/auth/login', 200, undefined, 'POST', {
    identifier: guardian.user.username, password: `Guardian-${label}-Password!`,
  });
  assert.equal(passwordLogin.user.id, guardian.user.id);
  const consumedStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${replacement.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(consumedStatus.status, 'consumed');
  await call('/v1/guardian-qr/enroll', 410, undefined, 'POST', {
    enrollmentToken: enrollmentClaim.enrollmentToken, fullName: `Guardian ${label}`,
    phone: '+4915112345678', password: `Guardian-${label}-Password!`,
    passwordConfirmation: `Guardian-${label}-Password!`,
  });
  const state = await call<{ guardian: { invitations: Array<{
    id: string; status: string; guardianUserId: string | null;
  }> } }>('/v1/me/guardian-verification', 200, candidate.token);
  const invitation = state.guardian.invitations.find((item) => item.guardianUserId === guardian.user.id);
  assert(invitation);
  assert.equal(invitation.status, 'registered');
  const candidateKey = await fingerprintKeyFor(candidate, invitation.id);
  await call(`/v1/guardian-invitations/${invitation.id}/contacts`, 400,
    candidate.token, 'PUT', { hashes: contactHashes(candidateKey) });
  await call(`/v1/guardian-invitations/${invitation.id}/contacts`, 200,
    candidate.token, 'PUT', { fingerprintVersion: 2, contacts: contactFingerprints(candidateKey) });
  const guardianKey = await fingerprintKeyFor(guardian, invitation.id);
  assert.equal(guardianKey, candidateKey);
  const verified = await call<{ status: string; commonContactCount: number }>(
    `/v1/guardian-invitations/${invitation.id}/contacts`, 200,
    guardian.token, 'PUT', { fingerprintVersion: 2, contacts: contactFingerprints(guardianKey) },
  );
  assert.equal(verified.status, 'verified');
  assert.equal(verified.commonContactCount, 7);
  const admin = await call<Session>('/auth/login', 200, undefined, 'POST', {
    email: config.seedAdminEmail, password: config.seedAdminPassword,
  });
  const reviewQueue = await call<{ links: Array<{ id: string; guardianUserId: string; status: string }> }>(
    '/v1/admin/guardian-links', 200, admin.token,
  );
  const review = reviewQueue.links.find((item) => item.guardianUserId === guardian.user.id);
  assert(review);
  assert.equal(review.status, 'verified');
  const guardianDetails = await call<{ links: Array<{
    id: string; verificationMethod: string; matchedContacts: unknown[];
  }> }>(`/v1/admin/guardians/${guardian.user.id}/details`, 200, admin.token);
  const verifiedDetails = guardianDetails.links.find((item) => item.id === invitation.id);
  assert.equal(verifiedDetails?.verificationMethod, 'contacts');
  assert.equal(verifiedDetails?.matchedContacts.length, 5);
  const reloginQr = await call<{ challengeId: string; payload: string }>(
    '/v1/me/guardian-qr', 201, candidate.token, 'POST',
  );
  const relogin = await call<{ kind: string } & Session>(
    '/v1/guardian-qr/claim', 200, undefined, 'POST', { payload: reloginQr.payload },
  );
  assert.equal(relogin.kind, 'login');
  assert.equal(relogin.user.id, guardian.user.id);
  const reloginStatus = await call<{ status: string }>(
    `/v1/me/guardian-qr/${reloginQr.challengeId}/status`, 200, candidate.token,
  );
  assert.equal(reloginStatus.status, 'consumed');
  return guardian;
};

export const completeProfile = (name: 'First' | 'Second') => ({
  displayName: `Smoke ${name}`,
  gender: name === 'First' ? 'man' : 'woman',
  birthYear: name === 'First' ? 1993 : 1997,
  birthPlace: 'Berlin',
  country: 'Germany',
  city: 'Berlin',
  religion: 'muslim',
  maritalStatus: 'single',
  childrenCount: 0,
  education: 'bachelor',
  occupation: 'Synthetic test occupation',
  skinTone: 'olive', eyeColor: 'brown', hairColor: 'black',
  heightCm: 175, weightKg: 72, bodyBuild: 'average', appearanceNote: '',
  bio: 'A synthetic profile used for an isolated API smoke test and no real matchmaking.',
  valuesText: 'Mutual respect, transparency, and family understanding.',
  musicPreferences: ['none'],
  partnerDescription: 'A synthetic partner description used to test reciprocal search.',
  preferredGender: name === 'First' ? 'woman' : 'man',
  preferredMinAge: 18,
  preferredMaxAge: 50,
  preferredCountry: 'any',
  preferredMaritalStatus: 'any',
  preferredEducation: 'any',
  preferredReligion: 'muslim',
  preferredEyeColors: ['any'],
  preferredHairColors: ['any'],
  acceptsPartnerChildren: true,
  preferredMaxChildren: 5,
  publicationRequested: true,
});
