import { Injectable, Logger, NotFoundException, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { LootProfile } from '../../entities/loot-profile.entity';
import { InstancesService } from '../instances/instances.service';
import { CreateLootProfileDto } from './dto/create-loot-profile.dto';
import { UpdateLootProfileDto } from './dto/update-loot-profile.dto';
import { ImportLootProfileDto } from './dto/import-loot-profile.dto';
import { RUST_CONTAINERS } from './data/rust-containers';

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

  constructor(
    @InjectRepository(LootProfile)
    private readonly lootRepo: Repository<LootProfile>,
    private readonly instancesService: InstancesService,
  ) {}

  /** List profiles for a license (summaries — no JSONB) */
  async getProfiles(licenseId: string) {
    const profiles = await this.lootRepo.find({
      where: { license_id: licenseId },
      select: ['id', 'profile_name', 'description', 'is_active', 'created_at', 'updated_at'],
      order: { created_at: 'DESC' },
    });
    return { profiles };
  }

  /** Get full profile with JSONB data */
  async getProfile(licenseId: string, profileId: string) {
    const profile = await this.lootRepo.findOne({
      where: { id: profileId, license_id: licenseId },
    });
    if (!profile) throw new NotFoundException('Loot profile not found');
    return { profile };
  }

  /** Create a new profile */
  async createProfile(licenseId: string, dto: CreateLootProfileDto) {
    const profile = this.lootRepo.create({
      license_id: licenseId,
      profile_name: dto.profile_name,
      description: dto.description || null,
      loot_table: dto.loot_table || {},
      loot_groups: dto.loot_groups || {},
    });
    const saved = await this.lootRepo.save(profile);
    return { profile: saved };
  }

  /** Update an existing profile */
  async updateProfile(licenseId: string, profileId: string, dto: UpdateLootProfileDto) {
    const profile = await this.lootRepo.findOne({
      where: { id: profileId, license_id: licenseId },
    });
    if (!profile) throw new NotFoundException('Loot profile not found');

    if (dto.profile_name !== undefined) profile.profile_name = dto.profile_name;
    if (dto.description !== undefined) profile.description = dto.description;
    if (dto.loot_table !== undefined) profile.loot_table = dto.loot_table;
    if (dto.loot_groups !== undefined) profile.loot_groups = dto.loot_groups;
    if (dto.is_active !== undefined) profile.is_active = dto.is_active;
    profile.updated_at = new Date();

    const saved = await this.lootRepo.save(profile);
    return { profile: saved };
  }

  /** Delete a profile */
  async deleteProfile(licenseId: string, profileId: string) {
    const result = await this.lootRepo.delete({ id: profileId, license_id: licenseId });
    if (result.affected === 0) throw new NotFoundException('Loot profile not found');
    return { deleted: true };
  }

  /** Duplicate a profile */
  async duplicateProfile(licenseId: string, profileId: string) {
    const source = await this.lootRepo.findOne({
      where: { id: profileId, license_id: licenseId },
    });
    if (!source) throw new NotFoundException('Loot profile not found');

    const copy = this.lootRepo.create({
      license_id: licenseId,
      profile_name: `${source.profile_name} (Copy)`,
      description: source.description,
      loot_table: JSON.parse(JSON.stringify(source.loot_table)),
      loot_groups: JSON.parse(JSON.stringify(source.loot_groups)),
      is_active: false,
    });
    const saved = await this.lootRepo.save(copy);
    return { profile: saved };
  }

  /** Apply profile to server with multiplier */
  async applyToServer(licenseId: string, profileId: string, multiplier: number) {
    const profile = await this.lootRepo.findOne({
      where: { id: profileId, license_id: licenseId },
    });
    if (!profile) throw new NotFoundException('Loot profile not found');

    // Deep clone and apply multiplier
    const scaledTable = JSON.parse(JSON.stringify(profile.loot_table));
    const scaledGroups = JSON.parse(JSON.stringify(profile.loot_groups));

    if (multiplier !== 1) {
      this.applyMultiplierToTable(scaledTable, multiplier);
      this.applyMultiplierToGroups(scaledGroups, multiplier);
    }

    const lootTablesJson = JSON.stringify(scaledTable, null, 2);
    const lootGroupsJson = JSON.stringify(scaledGroups, null, 2);

    try {
      // Write LootTables.json via Rust agent
      await this.instancesService.writeFileForLicense(
        licenseId,
        'oxide/data/BetterLoot/LootTables.json',
        lootTablesJson,
      );

      // Write LootGroups.json via Rust agent
      await this.instancesService.writeFileForLicense(
        licenseId,
        'oxide/data/BetterLoot/LootGroups.json',
        lootGroupsJson,
      );

      // Reload BetterLoot plugin via RCON
      await this.instancesService.rconForLicense(licenseId, 'oxide.reload BetterLoot');

      // Mark this profile as active, deactivate others
      await this.lootRepo.update({ license_id: licenseId }, { is_active: false });
      await this.lootRepo.update(
        { id: profileId, license_id: licenseId },
        { is_active: true, updated_at: new Date() },
      );

      return {
        success: true,
        message: `Profile "${profile.profile_name}" applied with ${multiplier}x multiplier`,
        profile_name: profile.profile_name,
        multiplier,
      };
    } catch (error) {
      this.logger.error(`Failed to apply loot profile: ${(error as Error).message}`);
      throw new HttpException(
        'Failed to apply loot profile — agent may be offline',
        HttpStatus.SERVICE_UNAVAILABLE,
      );
    }
  }

  /** Import BetterLoot/Looty JSON as a new profile */
  async importProfile(licenseId: string, dto: ImportLootProfileDto) {
    const profile = this.lootRepo.create({
      license_id: licenseId,
      profile_name: dto.profile_name,
      description: dto.description || 'Imported profile',
      loot_table: dto.loot_table,
      loot_groups: dto.loot_groups || {},
    });
    const saved = await this.lootRepo.save(profile);
    return { profile: saved };
  }

  /** Export profile as BetterLoot-compatible JSON with optional multiplier */
  async exportProfile(licenseId: string, profileId: string, multiplier: number) {
    const profile = await this.lootRepo.findOne({
      where: { id: profileId, license_id: licenseId },
    });
    if (!profile) throw new NotFoundException('Loot profile not found');

    const exportTable = JSON.parse(JSON.stringify(profile.loot_table));
    const exportGroups = JSON.parse(JSON.stringify(profile.loot_groups));

    if (multiplier && multiplier !== 1) {
      this.applyMultiplierToTable(exportTable, multiplier);
      this.applyMultiplierToGroups(exportGroups, multiplier);
    }

    return {
      profile_name: profile.profile_name,
      multiplier: multiplier || 1,
      loot_table: exportTable,
      loot_groups: exportGroups,
    };
  }

  /** Get static list of Rust container prefabs */
  getContainers() {
    return { containers: RUST_CONTAINERS };
  }

  // --- Multiplier helpers ---

  private applyMultiplierToTable(table: Record<string, any>, multiplier: number) {
    for (const prefab of Object.values(table)) {
      if (prefab?.ItemSettings) {
        this.scaleField(prefab.ItemSettings, 'ItemsMin', multiplier);
        this.scaleField(prefab.ItemSettings, 'ItemsMax', multiplier);
        this.scaleField(prefab.ItemSettings, 'MinScrap', multiplier);
        this.scaleField(prefab.ItemSettings, 'MaxScrap', multiplier);
      }
      if (prefab?.GuaranteedItems) {
        this.scaleItems(prefab.GuaranteedItems, multiplier);
      }
      if (prefab?.UngroupedItems) {
        this.scaleItems(prefab.UngroupedItems, multiplier);
      }
    }
  }

  private applyMultiplierToGroups(groups: Record<string, any>, multiplier: number) {
    for (const group of Object.values(groups)) {
      if (group?.GuaranteedItems) {
        this.scaleItems(group.GuaranteedItems, multiplier);
      }
      if (group?.ItemList) {
        this.scaleItems(group.ItemList, multiplier);
      }
    }
  }

  private scaleItems(items: Record<string, any>, multiplier: number) {
    for (const item of Object.values(items)) {
      this.scaleField(item, 'Min', multiplier);
      this.scaleField(item, 'Max', multiplier);
      // Recursively scale bonus items
      if (item?.BonusItems) {
        this.scaleItems(item.BonusItems, multiplier);
      }
    }
  }

  private scaleField(obj: Record<string, any>, field: string, multiplier: number) {
    if (typeof obj[field] === 'number') {
      obj[field] = Math.round(obj[field] * multiplier);
    }
  }
}
