/**
 * AI Usage Integration Test Helpers
 *
 * 共享：建 org + user + role + 5 个 ai-usage 权限点的挂接
 *
 * 关键 env override（在 import 链解析前生效）：
 *   AUTH_THROTTLER_LIMIT：生产 5 req/30s 是登录限流，测试套件 beforeEach 会
 *   连发 2 次 login (admin+member)，跑 3 个 test 就触限，login 返回 429 →
 *   accessToken 为 undefined → 后续 Bearer "" 全部 401。此处抬到 10000
 *   让测试跑得通；生产 .env 不受影响。
 */
process.env.AUTH_THROTTLER_LIMIT = process.env.AUTH_THROTTLER_LIMIT || '10000';

import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import request from 'supertest';
import { INestApplication } from '@nestjs/common';
import { createTestUser, randomString } from '../../helpers/factories/user.factory';

/**
 * 本 helper 内部建 org（不用 createTestOrganization factory：现有 factory 引用了
 * Organization 不再存在的 `shortName`/`legalName` 字段，会导致 prisma 校验失败；
 * 此为既有 schema drift，超出本 PR 范围 — 见 issue tracker）
 */
export async function createOrgLocal(): Promise<any> {
  return prisma.organization.create({
    data: {
      code: `t_org_${randomString()}`,
      name: `测试组织_${randomString()}`,
      status: 'ACTIVE',
    },
  });
}

const prisma = new PrismaClient();

const AI_USAGE_PERMISSIONS = [
  { resource: 'ai-usage', action: 'view-own' },
  { resource: 'ai-usage', action: 'view-all' },
  { resource: 'ai-usage', action: 'manage-tokens-own' },
  { resource: 'ai-usage', action: 'manage-tokens-all' },
  { resource: 'ai-usage', action: 'block-device' },
  { resource: 'ai-usage', action: 'export' },
];

/**
 * 确保 5 个 ai-usage 权限点存在（如 seed 未跑过则补建）
 */
export async function ensureAiUsagePermissions(): Promise<Record<string, string>> {
  const ids: Record<string, string> = {};
  for (const p of AI_USAGE_PERMISSIONS) {
    const code = `${p.resource}:${p.action}`;
    const perm = await prisma.permission.upsert({
      where: { resource_action: { resource: p.resource, action: p.action } },
      update: {},
      create: {
        resource: p.resource,
        action: p.action,
        description: `test-${code}`,
        isBuiltIn: true,
      },
    });
    ids[code] = perm.id;
  }
  return ids;
}

export interface AiUsageTestContext {
  organization: any;
  organizationId: string;
  adminUser: any;
  adminToken: string;
  memberUser: any;
  memberToken: string;
}

/**
 * 现有 JWT 中间件通过 `X-Organization-Id` header 决定 currentOrganizationId；
 * 后端控制器读 `req.user.currentOrganizationId`。集成测试必须发该 header。
 */
export function withOrg(req: any, ctx: AiUsageTestContext) {
  return req.set('X-Organization-Id', ctx.organizationId);
}

/**
 * 建一个 org + admin user（拥有全部 5 权限）+ member user（只有 view-own）
 */
export async function setupAiUsageTestContext(app: INestApplication): Promise<AiUsageTestContext> {
  const permIds = await ensureAiUsagePermissions();

  const org = await createOrgLocal();

  // Admin role：5 全
  const adminSuffix = randomString();
  const adminRole = await prisma.role.create({
    data: {
      code: `R_ADMIN_AI_${adminSuffix}`,
      name: `AI Usage Admin (test) ${adminSuffix}`,
      enabled: true,
    },
  });
  for (const pid of Object.values(permIds)) {
    await prisma.rolePermission.create({ data: { roleId: adminRole.id, permissionId: pid } });
  }

  // Member role：只 view-own
  const memberSuffix = randomString();
  const memberRole = await prisma.role.create({
    data: {
      code: `R_MEMBER_AI_${memberSuffix}`,
      name: `AI Usage Member (test) ${memberSuffix}`,
      enabled: true,
    },
  });
  // member 同时拿 view-own（读）+ manage-tokens-own（管理自己的 ffctk token）
  await prisma.rolePermission.create({
    data: { roleId: memberRole.id, permissionId: permIds['ai-usage:view-own'] },
  });
  await prisma.rolePermission.create({
    data: { roleId: memberRole.id, permissionId: permIds['ai-usage:manage-tokens-own'] },
  });

  const password = 'TestPass123!';
  // User 模型多租户通过 UserRole.organizationId 关联，不在 user 表上有直接字段
  const adminUser = await createTestUser({ password });
  const memberUser = await createTestUser({ password });

  await prisma.userRole.create({
    data: { userId: adminUser.id, roleId: adminRole.id, organizationId: org.id },
  });
  await prisma.userRole.create({
    data: { userId: memberUser.id, roleId: memberRole.id, organizationId: org.id },
  });

  // login both
  const loginAdmin = await request(app.getHttpServer())
    .post('/api/v1/auth/login')
    .send({ username: adminUser.username, password });
  const loginMember = await request(app.getHttpServer())
    .post('/api/v1/auth/login')
    .send({ username: memberUser.username, password });

  return {
    organization: org,
    organizationId: org.id,
    adminUser,
    adminToken: loginAdmin.body?.data?.accessToken ?? '',
    memberUser,
    memberToken: loginMember.body?.data?.accessToken ?? '',
  };
}

/**
 * 直接在 DB 里建一条 ai-usage event，bypass ingestion
 * 用于 dashboard 测试快速制造数据
 */
export async function seedEvent(opts: {
  userId: string;
  organizationId: string;
  deviceId?: string;
  tool?: 'claude-code' | 'codex-cli';
  model?: string;
  projectPath?: string;
  sessionId?: string;
  ts?: Date;
  inputTokens?: number;
  outputTokens?: number;
  cacheCreationTokens?: number;
  cacheReadTokens?: number;
  costUsd?: string;
  // v1.1 富 metadata（全部可选）
  gitBranch?: string;
  agentVersionEvent?: string;
  worktreeLabel?: string;
  cwdBasename?: string;
  turnIndex?: number;
  toolUseCount?: number;
  toolNames?: string[];
  stopReason?: string;
  serviceTier?: string;
}) {
  // 必须先有 device
  let device = await prisma.aiUsageDevice.findFirst({
    where: { userId: opts.userId, organizationId: opts.organizationId },
  });
  if (!device) {
    device = await prisma.aiUsageDevice.create({
      data: {
        deviceId: `dev-${randomString()}`,
        userId: opts.userId,
        organizationId: opts.organizationId,
        createdById: opts.userId,
        hostname: 'test-host',
        osPlatform: 'LINUX',
        firstSeenAt: new Date(),
        lastSeenAt: new Date(),
      },
    });
  }

  const rawMsgId = `seed:${randomString()}:${Date.now()}`;
  const tool = opts.tool ?? 'claude-code';
  const model = opts.model ?? 'claude-opus-4-7';
  const projectPath = opts.projectPath ?? '/home/test/workspace';
  const ts = opts.ts ?? new Date();
  const input = opts.inputTokens ?? 1000;
  const output = opts.outputTokens ?? 500;
  const cacheCreate = opts.cacheCreationTokens ?? 0;
  const cacheRead = opts.cacheReadTokens ?? 0;
  const cost = opts.costUsd ?? '0.001000';
  const sessionId = opts.sessionId ?? `s-${randomString()}`;
  await prisma.$executeRawUnsafe(
    `INSERT INTO platform_ai_usage.ai_usage_events
       (id, raw_message_id, device_id, user_id, tool, session_id, project_path, project_basename, model, ts, received_at,
        input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, estimated_cost_usd,
        git_branch, agent_version_event, worktree_label, cwd_basename, turn_index, tool_use_count, tool_names, stop_reason, service_tier,
        organization_id, created_by_id, updated_at)
     VALUES (gen_random_uuid(),$1,$2::uuid,$3::uuid,$4::"platform_ai_usage"."AiUsageTool",
             $5,$6,$7,$8,$9::timestamptz,NOW(),$10,$11,$12,$13,$14::numeric,
             $16,$17,$18,$19,$20,$21,$22::jsonb,$23,$24,
             $15::uuid,$3::uuid,NOW())`,
    rawMsgId,
    device.id,
    opts.userId,
    tool,
    sessionId,
    projectPath,
    projectPath.split('/').pop() || projectPath,
    model,
    ts,
    input,
    output,
    cacheCreate,
    cacheRead,
    cost,
    opts.organizationId,
    opts.gitBranch ?? null,
    opts.agentVersionEvent ?? null,
    opts.worktreeLabel ?? null,
    opts.cwdBasename ?? null,
    opts.turnIndex ?? null,
    opts.toolUseCount ?? null,
    opts.toolNames != null ? JSON.stringify(opts.toolNames) : null,
    opts.stopReason ?? null,
    opts.serviceTier ?? null,
  );
  return { deviceId: device.id, rawMessageId: rawMsgId, sessionId };
}

/**
 * 清理本 helper 创建的所有 ai-usage 测试数据。
 * 遵循 CLAUDE.md「测试数据生命周期：cleanup 优先用前缀过滤」约定 — 全部按 hostname 关联到
 * 本 helper 注册的 device，并联清 DLQ；并行 CI worker 共享 testdb 时彼此不破坏。
 */
export async function cleanupAiUsageData() {
  await prisma.$executeRawUnsafe(
    `DELETE FROM platform_ai_usage.ai_usage_events WHERE raw_message_id LIKE 'seed:%' OR raw_message_id LIKE 'test:%' OR raw_message_id LIKE 'b:%' OR raw_message_id LIKE 'test-rich:%' OR raw_message_id LIKE 'test-legacy:%' OR raw_message_id LIKE 'filt-%' OR raw_message_id LIKE 'gap-%' OR raw_message_id LIKE 'turns-%'`,
  ).catch((e: any) => console.warn('[ai-usage cleanup] events:', e?.message));
  // DLQ 行通过 device_id 关联到本 helper 注册的 test- 前缀 device；先取这些 device 的 id，再清 DLQ
  await prisma
    .$executeRawUnsafe(
      `DELETE FROM platform_ai_usage.ai_usage_event_dlq
       WHERE device_id IN (
         SELECT id FROM platform_ai_usage.ai_usage_devices WHERE hostname LIKE 'test-%' OR hostname LIKE 'test_%'
       )`,
    )
    .catch((e: any) => console.warn('[ai-usage cleanup] dlq:', e?.message));
  await prisma.aiUsageDevice.deleteMany({ where: { hostname: { startsWith: 'test-' } } }).catch((e) => console.warn('[ai-usage cleanup] devices:', e?.message));
  await prisma.aiUsageToken.deleteMany({ where: { name: { startsWith: 'test-' } } }).catch((e) => console.warn('[ai-usage cleanup] tokens:', e?.message));
}

export async function disconnect() {
  await prisma.$disconnect();
}
