import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PlayerAction } from '../../entities/player-action.entity';
import { PlayerSession } from '../../entities/player-session.entity';
import { NatsService } from '../../services/nats.service';
import { PlayerActionDto } from './dto/player-action.dto';

export interface Player {
  steam_id: string;
  player_name: string;
  status: 'online' | 'offline' | 'banned';
  last_seen?: Date;
  ban_expires?: Date | null;
  total_sessions?: number;
  total_playtime_seconds?: number;
}

@Injectable()
export class PlayersService {
  constructor(
    @InjectRepository(PlayerAction)
    private readonly actionRepo: Repository<PlayerAction>,
    @InjectRepository(PlayerSession)
    private readonly sessionRepo: Repository<PlayerSession>,
    private readonly natsService: NatsService,
  ) {}

  /**
   * Get players for a license.
   *
   * Primary source: player_sessions table (tracks session lifecycles).
   * Secondary source: player_actions table (determines ban status).
   * A player whose most recent action is a 'ban' (not subsequently 'unban') is shown as banned.
   * A player with an open session (session_end IS NULL) is shown as online.
   */
  async getPlayers(licenseId: string): Promise<{ players: Player[] }> {
    // Get distinct players from session history
    const sessions = await this.sessionRepo
      .createQueryBuilder('session')
      .where('session.license_id = :licenseId', { licenseId })
      .orderBy('session.session_start', 'DESC')
      .getMany();

    // Build per-player session aggregates
    const playerMap = new Map<string, Player>();

    for (const session of sessions) {
      if (!playerMap.has(session.steam_id)) {
        const isOnline = session.session_end === null;
        playerMap.set(session.steam_id, {
          steam_id: session.steam_id,
          player_name: session.player_name,
          status: isOnline ? 'online' : 'offline',
          last_seen: session.session_start,
          ban_expires: null,
          total_sessions: 0,
          total_playtime_seconds: 0,
        });
      }

      const entry = playerMap.get(session.steam_id)!;
      entry.total_sessions = (entry.total_sessions || 0) + 1;
      entry.total_playtime_seconds = (entry.total_playtime_seconds || 0) + (session.duration_seconds || 0);
    }

    // Overlay ban status from most recent action per player
    const recentActions = await this.actionRepo
      .createQueryBuilder('action')
      .where('action.license_id = :licenseId', { licenseId })
      .orderBy('action.created_at', 'DESC')
      .getMany();

    // Track the most recent action per steam_id to determine ban state
    const latestActionBySteamId = new Map<string, PlayerAction>();
    for (const action of recentActions) {
      if (!latestActionBySteamId.has(action.steam_id)) {
        latestActionBySteamId.set(action.steam_id, action);
      }
    }

    for (const [steamId, action] of latestActionBySteamId) {
      if (action.action_type === 'ban') {
        // Add player from actions even if they have no sessions
        if (!playerMap.has(steamId)) {
          playerMap.set(steamId, {
            steam_id: action.steam_id,
            player_name: action.player_name,
            status: 'banned',
            last_seen: action.created_at,
            ban_expires: action.duration_minutes
              ? new Date(action.created_at.getTime() + action.duration_minutes * 60000)
              : null,
            total_sessions: 0,
            total_playtime_seconds: 0,
          });
        } else {
          const entry = playerMap.get(steamId)!;
          entry.status = 'banned';
          entry.ban_expires = action.duration_minutes
            ? new Date(action.created_at.getTime() + action.duration_minutes * 60000)
            : null;
        }
      }
    }

    const players = Array.from(playerMap.values());

    return { players };
  }

  /**
   * Perform a moderation action on a player.
   * Supported actions: kick, ban, unban, warn, note.
   * kick/ban/unban are forwarded to the game server via NATS.
   */
  async performAction(
    licenseId: string,
    userId: string,
    dto: PlayerActionDto,
  ): Promise<{ success: boolean }> {
    // Insert action record
    const action = this.actionRepo.create({
      license_id: licenseId,
      steam_id: dto.steam_id,
      player_name: dto.player_name,
      action_type: dto.action_type,
      reason: dto.reason || null,
      duration_minutes: dto.duration_minutes || null,
      performed_by: userId,
    });

    await this.actionRepo.save(action);

    // Forward kick, ban, and unban to the game server via NATS
    if (dto.action_type === 'kick' || dto.action_type === 'ban' || dto.action_type === 'unban') {
      await this.natsService.sendServerCommand(licenseId, dto.action_type, {
        steam_id: dto.steam_id,
        reason: dto.reason,
        duration_minutes: dto.duration_minutes,
      });
    }

    return { success: true };
  }
}
