/**
 * e2e 共用 fixture：玩家 / 超管登录、API 客户端、期号工具。
 *
 * 设计原则：
 *  - 走真 HTTP（request.newContext），不绕开鉴权
 *  - token 由各用例按需获取（每个 spec 自带 reset），不跨测试串味
 *  - 任何 SQL 写操作走真 PG；不允许 Http::fake / Bus::fake
 */

import { request as pwRequest, type APIRequestContext, type APIResponse } from '@playwright/test';

export interface PlayerSession {
  accessToken: string;
  expiresIn: number;
  player: { id: number; site_code: string; username: string; funding_mode: string; auth_source: string };
}

export interface AdminSession {
  accessToken: string;
  tokenType: string;
  expiresIn: number;
  admin: { id: number; username: string; is_super_admin: boolean };
}

const API = process.env.PLAYWRIGHT_API_URL ?? 'http://127.0.0.1:8000';

export function apiUrl(path: string): string {
  return new URL(path, API + '/').toString();
}

export async function expectOk<T = any>(resp: APIResponse, hint = ''): Promise<T> {
  if (!resp.ok()) {
    const body = await resp.text().catch(() => '');
    throw new Error(`HTTP ${resp.status()} ${hint}\nURL: ${resp.url()}\nBody: ${body.slice(0, 800)}`);
  }
  return resp.json() as Promise<T>;
}

export async function playerLogin(password?: string): Promise<PlayerSession> {
  const ctx = await pwRequest.newContext({ baseURL: API });
  const resp = await ctx.post('/api/v1/player/auth/login', {
    data: {
      site_code: process.env.E2E_PLAYER_SITE_CODE ?? 'demo',
      username: process.env.E2E_PLAYER_USERNAME ?? 'demo_player',
      password: password ?? process.env.E2E_PLAYER_PASSWORD ?? '12345678',
    },
  });
  const data = await expectOk<{ code: number; data: PlayerSession; msg?: string }>(resp, 'player login');
  await ctx.dispose();
  if (!data.data?.access_token) throw new Error('login ok but no access_token: ' + JSON.stringify(data));
  return data.data;
}

export async function adminLogin(): Promise<AdminSession> {
  const ctx = await pwRequest.newContext({ baseURL: API });
  const captchaResp = await ctx.get('/api/v1/admin/auth/captcha');
  const captchaBody = (await captchaResp.json()) as { data: { captcha_key: string } };
  const resp = await ctx.post('/api/v1/admin/auth/login', {
    data: {
      account: process.env.E2E_ADMIN_USERNAME ?? 'admin',
      password: process.env.E2E_ADMIN_PASSWORD ?? '12345678',
      captcha_key: captchaBody.data.captcha_key,
      captcha_code: 'LOTTERY_E2E_BYPASS',
    },
  });
  const data = await expectOk<{ code: number; data: AdminSession; msg?: string }>(resp, 'admin login');
  await ctx.dispose();
  if (!data.data?.access_token) throw new Error('admin login ok but no access_token: ' + JSON.stringify(data));
  return data.data;
}

export async function playerCtx(token: string): Promise<APIRequestContext> {
  return pwRequest.newContext({
    baseURL: API,
    extraHTTPHeaders: { Authorization: `Bearer ${token}` },
  });
}

export async function adminCtx(token: string): Promise<APIRequestContext> {
  return pwRequest.newContext({
    baseURL: API,
    extraHTTPHeaders: { Authorization: `Bearer ${token}` },
  });
}

/** 取当前开放、最近尚未开奖的期号（公开接口，无需登录）。
 * 返回 null 表示当前没有可下注期号（大厅空），调用方应跳过下注用例。
 */
export async function fetchCurrentDrawNo(): Promise<{ draw_no: string; status: string | number; close_time?: string } | null> {
  const ctx = await pwRequest.newContext({ baseURL: API });
  const resp = await ctx.get('/api/v1/draw/current');
  const body = await expectOk<{
    code: number;
    data: {
      server_now_ms?: number;
      data: { draw_no?: string; status?: string | number; close_time?: string } | null;
    };
  }>(resp, 'draw/current');
  await ctx.dispose();
  const snapshot = body.data?.data;
  if (!snapshot?.draw_no) return null;
  return snapshot as { draw_no: string; status: string | number; close_time?: string };
}

/** 取玩法/赔率/位元（e2e 验证玩家下注时拼 bet payload 正确） */
export async function fetchPlayEffective(): Promise<any> {
  const ctx = await pwRequest.newContext({ baseURL: API });
  const resp = await ctx.get('/api/v1/play/effective');
  const data = await expectOk<{ code: number; data: any }>(resp, 'play/effective');
  await ctx.dispose();
  return data.data;
}

/** 通过 /api/v1/_e2e/* 调一个 e2e 辅助端点（POST/GET 通用） */
export async function e2e<T = any>(
  method: 'GET' | 'POST',
  path: string,
  body?: Record<string, any>,
): Promise<T> {
  const ctx = await pwRequest.newContext({ baseURL: API });
  const resp = await ctx.fetch(`/api/v1/_e2e${path}`, {
    method,
    data: body ?? {},
    headers: { 'Content-Type': 'application/json' },
  });
  const data = await expectOk<T>(resp, `e2e ${method} ${path}`);
  await ctx.dispose();
  return data;
}
