import { BadRequestException, ForbiddenException, Inject, Injectable, Logger, OnModuleInit, Optional } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DataSource } from 'typeorm';
import { randomUUID } from 'crypto';
import { AccountProfileService } from '../auth/account_profile.service';
import { SocialService } from '../social/social.service';
import { ChatWebSocketGateway } from '../../gateway/chat/chat.gateway';
import { Users } from '../../core/db/users/users.entity';
import { AdminOperationLogService } from './admin_operation_log.service';
import {
  clearTestLabPresences,
  getAllTestLabPresences,
  getTestLabPresence,
  TestLabPresence,
} from './test_lab_presence.registry';
import { CreateTestLabActorDto, TestLabMessageDto, TestLabRoomPolicyDto, TestLabSocialActionDto, UpdateTestLabActorDto } from './test_lab.dto';

type UserStore = {
  create(input: Partial<Users> & { username: string; nickname: string }): Promise<Users>;
  findOne(id: bigint): Promise<Users>;
  findAll(limit: number, offset: number): Promise<Users[]>;
  remove(id: bigint): Promise<unknown>;
};

type AdminActor = { adminId: string; username: string };

const MAX_TEST_ACTORS = 20;

@Injectable()
export class TestLabService implements OnModuleInit {
  private readonly logger = new Logger(TestLabService.name);

  constructor(
    private readonly config: ConfigService,
    @Inject('UsersService') private readonly users: UserStore,
    private readonly accountProfiles: AccountProfileService,
    private readonly social: SocialService,
    private readonly gateway: ChatWebSocketGateway,
    private readonly audit: AdminOperationLogService,
    @Optional() private readonly dataSource?: DataSource,
  ) {}

  async onModuleInit(): Promise<void> {
    if (this.isTestEnvironment()) {
      await this.clearTestAccounts('startup');
    }
  }

  isEnabled(): boolean {
    return this.isTestEnvironment() && this.config.get<string>('TEST_LAB_ENABLED', 'false') === 'true';
  }

  async status() {
    this.assertEnabled();
    const actors = await this.listActorsInternal();
    return {
      enabled: this.isEnabled(),
      environment: this.config.get<string>('NODE_ENV', 'development'),
      maxActors: MAX_TEST_ACTORS,
      actors,
      onlinePlayers: this.gateway.getOnlineWorldPlayers(),
    };
  }

  async createActor(dto: CreateTestLabActorDto, admin: AdminActor) {
    this.assertEnabled();
    const actors = await this.listActorsInternal();
    if (actors.length >= MAX_TEST_ACTORS) throw new BadRequestException(`最多创建 ${MAX_TEST_ACTORS} 个测试假人`);

    const nickname = dto.nickname.trim();
    const user = await this.users.create({
      username: `test_actor_${randomUUID().replace(/-/g, '').slice(0, 18)}`,
      nickname,
      role: 1,
      email_verified: true,
      is_test_account: true,
    });
    await this.accountProfiles.ensureProfile(user.id, dto.skinId || 'classic_whale');
    await this.accountProfiles.updateAccountProfile(user.id, { settings: { room_visit_policy: 'public' } });

    const presence: TestLabPresence = {
      userId: user.id.toString(),
      username: user.username,
      nickname: user.nickname,
      mapId: dto.mapId,
      x: dto.x,
      y: dto.y,
      skinId: dto.skinId || 'classic_whale',
      avatarId: 'default',
      online: true,
    };
    this.gateway.setTestLabPresence(presence);
    await this.record(admin, 'CREATE', 'test_lab_actor', user.id.toString(), '创建测试假人');
    return await this.serializeActor(user, presence);
  }

  async updateActor(userId: string, dto: UpdateTestLabActorDto, admin: AdminActor) {
    this.assertEnabled();
    const user = await this.requireActor(userId);
    const current = getTestLabPresence(user.id) || this.offlinePresence(user);
    const next: TestLabPresence = {
      ...current,
      online: dto.online ?? current.online,
      mapId: dto.mapId ?? current.mapId,
      x: dto.x ?? current.x,
      y: dto.y ?? current.y,
      skinId: dto.skinId ?? current.skinId,
    };
    this.gateway.setTestLabPresence(next);
    await this.record(admin, 'UPDATE', 'test_lab_actor', userId, '更新测试假人状态');
    return await this.serializeActor(user, next);
  }

  async setRoomPolicy(userId: string, dto: TestLabRoomPolicyDto, admin: AdminActor) {
    this.assertEnabled();
    const user = await this.requireActor(userId);
    await this.accountProfiles.updateAccountProfile(user.id, { settings: { room_visit_policy: dto.roomVisitPolicy } });
    await this.record(admin, 'UPDATE', 'test_lab_actor', userId, '更新测试假人房间访问策略');
    return await this.serializeActor(user, getTestLabPresence(user.id) || this.offlinePresence(user));
  }

  async sendMessage(userId: string, dto: TestLabMessageDto, admin: AdminActor) {
    this.assertEnabled();
    const actor = await this.requireOnlineActor(userId);
    const content = dto.content.trim();
    if (dto.scope === 'private') {
      const targetId = this.requireOnlineTarget(dto.targetUserId || '');
      await this.social.sendTestDirectMessage(actor.id, BigInt(targetId), content);
    } else {
      this.gateway.broadcastTestLabChat(getTestLabPresence(actor.id) as TestLabPresence, content, dto.scope);
    }
    await this.record(admin, 'CREATE', 'test_lab_message', userId, `测试假人发送${dto.scope === 'private' ? '私聊' : '公共消息'}`);
    return { success: true };
  }

  async socialAction(userId: string, dto: TestLabSocialActionDto, admin: AdminActor) {
    this.assertEnabled();
    const actor = await this.requireOnlineActor(userId);
    const targetId = this.requireOnlineTarget(dto.targetUserId);
    const target = BigInt(targetId);
    let result: unknown;
    if (dto.action === 'friend_request') result = await this.social.createTestFriendRequest(actor.id, target);
    else if (dto.action === 'friend_accept') result = await this.social.respondToTestFriendRequest(actor.id, target, true);
    else if (dto.action === 'friend_reject') result = await this.social.respondToTestFriendRequest(actor.id, target, false);
    else result = await this.social.blockUser(actor.id, target);
    await this.record(admin, 'UPDATE', 'test_lab_social', userId, `测试假人执行${dto.action}`);
    return result;
  }

  async removeActor(userId: string, admin: AdminActor) {
    this.assertEnabled();
    const user = await this.requireActor(userId);
    this.gateway.removeTestLabActor(user.id.toString());
    await this.removeAccounts([user]);
    await this.record(admin, 'DELETE', 'test_lab_actor', userId, '删除测试假人');
    return { success: true };
  }

  async clear(admin: AdminActor) {
    this.assertEnabled();
    const count = await this.clearTestAccounts('manual');
    await this.record(admin, 'DELETE', 'test_lab_actor', undefined, `清空 ${count} 个测试假人`);
    return { success: true, count };
  }

  private isTestEnvironment(): boolean {
    const environment = this.config.get<string>('NODE_ENV', 'development');
    return environment === 'development' || environment === 'test';
  }

  private assertEnabled(): void {
    if (!this.isEnabled()) throw new ForbiddenException('测试实验室仅在已启用的开发/测试环境可用');
  }

  private async listActorsInternal() {
    const users = await this.users.findAll(MAX_TEST_ACTORS + 20, 0);
    return Promise.all(users
      .filter((user) => user.is_test_account === true)
      .map((user) => this.serializeActor(user, getTestLabPresence(user.id) || this.offlinePresence(user))));
  }

  private async requireActor(userId: string): Promise<Users> {
    if (!/^\d+$/.test(userId)) throw new BadRequestException('测试假人用户 ID 无效');
    const user = await this.users.findOne(BigInt(userId));
    if (user.is_test_account !== true) throw new ForbiddenException('目标不是测试实验室账号');
    return user;
  }

  private async requireOnlineActor(userId: string): Promise<Users> {
    const actor = await this.requireActor(userId);
    if (!getTestLabPresence(actor.id)?.online) throw new BadRequestException('测试假人当前不在线');
    return actor;
  }

  private requireOnlineTarget(userId: string): string {
    if (!/^\d+$/.test(userId)) throw new BadRequestException('目标用户 ID 无效');
    if (!this.gateway.getOnlineWorldPlayers().some((player) => player.userId === userId)) {
      throw new BadRequestException('目标玩家当前不在线');
    }
    return userId;
  }

  private offlinePresence(user: Users): TestLabPresence {
    return {
      userId: user.id.toString(), username: user.username, nickname: user.nickname,
      mapId: 'whale_port', x: 1280, y: 960, skinId: 'classic_whale', avatarId: 'default', online: false,
    };
  }

  private async serializeActor(user: Users, presence: TestLabPresence) {
    const account = await this.accountProfiles.getAccountProfile(user.id);
    const configuredPolicy = account.profile.settings?.room_visit_policy;
    const roomVisitPolicy = configuredPolicy === 'friends' || configuredPolicy === 'public' || configuredPolicy === 'closed'
      ? configuredPolicy
      : 'friends';
    return {
      userId: user.id.toString(), username: user.username, nickname: user.nickname,
      mapId: presence.mapId, x: presence.x, y: presence.y,
      skinId: presence.skinId, avatarId: presence.avatarId, online: presence.online, roomVisitPolicy,
    };
  }

  private async clearTestAccounts(reason: 'startup' | 'manual'): Promise<number> {
    const users = (await this.users.findAll(MAX_TEST_ACTORS + 20, 0)).filter((user) => user.is_test_account === true);
    if (users.length === 0) {
      if (reason === 'startup') clearTestLabPresences();
      return 0;
    }
    for (const presence of getAllTestLabPresences()) this.gateway.removeTestLabActor(presence.userId);
    await this.removeAccounts(users);
    this.logger.log(`测试实验室已清理 ${users.length} 个假人账号`, { reason });
    return users.length;
  }

  private async removeAccounts(users: Users[]): Promise<void> {
    const ids = users.map((user) => user.id);
    for (const user of users) this.gateway.removeTestLabActor(user.id.toString());
    await this.social.purgeTestUsers(ids);
    await this.purgeDomainRows(ids);
    for (const user of users) await this.users.remove(user.id);
  }

  private async purgeDomainRows(ids: bigint[]): Promise<void> {
    if (!this.dataSource || ids.length === 0) return;
    const values = ids.map((id) => id.toString());
    const placeholders = values.map(() => '?').join(', ');
    const runner = this.dataSource.createQueryRunner();
    await runner.connect();
    try {
      for (const table of ['room_decor_placements', 'user_assets', 'wallet_transactions', 'user_wallets', 'player_task_progress', 'user_profiles']) {
        try {
          await runner.query(`DELETE FROM \`${table}\` WHERE \`user_id\` IN (${placeholders})`, values);
        } catch (error) {
          this.logger.debug(`测试实验室清理跳过不可用表 ${table}`, { error: error instanceof Error ? error.message : String(error) });
        }
      }
    } finally {
      await runner.release();
    }
  }

  private async record(admin: AdminActor, operation: 'CREATE' | 'UPDATE' | 'DELETE', targetType: string, targetId: string | undefined, description: string): Promise<void> {
    try {
      await this.audit.createLog({
        adminUserId: admin.adminId,
        adminUsername: admin.username,
        operationType: operation,
        targetType,
        targetId,
        operationDescription: description,
        httpMethodPath: '/admin/test-lab',
        operationResult: 'SUCCESS',
        durationMs: 0,
        requestId: randomUUID(),
        context: { testLab: true },
      });
    } catch (error) {
      this.logger.warn('测试实验室操作日志写入失败', { error: error instanceof Error ? error.message : String(error) });
    }
  }
}
