import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { InventoryService } from '../player/inventory.service';
import { SaveRoomDecorPlacementDto } from './dto/save_room_decor_placement.dto';
import {
  ROOM_DECOR_BED_DEFAULT_SCALE,
  ROOM_DECOR_DEFINITIONS,
  ROOM_DECOR_LEGACY_DEFAULTS,
  ROOM_DECOR_LEGACY_BED_MAX_SCALE,
  ROOM_DECOR_LEGACY_WALL_DECOR_SCALES,
  ROOM_DECOR_ROOM_SCALE,
  findRoomDecorDefinition,
} from './room_decor_catalog';

interface UserRoomDecorRow {
  decor_id: string;
  placed: boolean;
  position_x: number | null;
  position_y: number | null;
  scale: number;
  z_index: number;
}

interface IRoomDecorPlacementsService {
  listPlacements(userId: bigint): Promise<UserRoomDecorRow[]>;
  savePlacement(userId: bigint, placement: SaveRoomDecorPlacementDto): Promise<UserRoomDecorRow>;
}

interface RoomDecorPayloadPlacement {
  position_x: number | null;
  position_y: number | null;
  scale: number;
}

@Injectable()
export class RoomDecorService {
  constructor(
    @Inject('IRoomDecorPlacementsService') private readonly roomDecorPlacementsService: IRoomDecorPlacementsService,
    private readonly inventoryService: InventoryService,
  ) {}

  async getInventory(userId: bigint) {
    const [inventory, placements] = await Promise.all([
      this.inventoryService.listInventory(userId, 'room_decor'),
      this.roomDecorPlacementsService.listPlacements(userId),
    ]);
    const placementByDecorId = new Map(placements.map((row) => [row.decor_id, row]));
    const rows = inventory.room_decor_ids.map((decorId) => {
      const definition = findRoomDecorDefinition(decorId);
      const placement = placementByDecorId.get(decorId);
      return placement ?? {
        decor_id: decorId,
        placed: false,
        position_x: definition?.default_position.x ?? null,
        position_y: definition?.default_position.y ?? null,
        scale: definition?.default_scale ?? 1,
        z_index: definition?.default_z_index ?? 0,
      };
    });
    return {
      items: rows
        .filter((row) => findRoomDecorDefinition(row.decor_id))
        .map((row) => this.toPayload(row)),
      definitions: ROOM_DECOR_DEFINITIONS,
    };
  }

  async savePlacement(userId: bigint, placement: SaveRoomDecorPlacementDto) {
    const definition = findRoomDecorDefinition(placement.decor_id);
    if (!definition) {
      throw new BadRequestException('摆件不存在或暂未开放');
    }
    if (!(await this.inventoryService.hasAsset(userId, 'room_decor', placement.decor_id))) {
      throw new BadRequestException('尚未拥有该房间摆件');
    }
    const row = await this.roomDecorPlacementsService.savePlacement(userId, {
      ...placement,
      scale: placement.scale ?? definition.default_scale,
      z_index: placement.z_index ?? definition.default_z_index,
    });
    return this.toPayload(row);
  }

  private toPayload(row: UserRoomDecorRow) {
    const definition = findRoomDecorDefinition(row.decor_id);
    const placement = this.normalizedPlacement(row, definition);
    return {
      decor_id: row.decor_id,
      name: definition?.name ?? row.decor_id,
      item_id: definition?.item_id ?? '',
      icon: definition?.icon ?? '',
      texture: definition?.texture ?? definition?.icon ?? '',
      placed: row.placed,
      position_x: placement.position_x,
      position_y: placement.position_y,
      scale: placement.scale,
      z_index: row.z_index ?? definition?.default_z_index ?? 0,
      default_position: definition?.default_position ?? { x: 0, y: 0 },
      default_scale: definition?.default_scale ?? 1,
      default_z_index: definition?.default_z_index ?? 0,
    };
  }

  private normalizedPlacement(
    row: UserRoomDecorRow,
    definition?: { default_scale: number; default_position: { x: number; y: number } },
  ): RoomDecorPayloadPlacement {
    const usesLegacyPlacement = this.usesLegacyPlacement(row);
    return {
      position_x: this.normalizedPositionValue(row.position_x, definition?.default_position.x ?? 0, usesLegacyPlacement),
      position_y: this.normalizedPositionValue(row.position_y, definition?.default_position.y ?? 0, usesLegacyPlacement),
      scale: this.normalizedScale(row, definition),
    };
  }

  private normalizedScale(row: UserRoomDecorRow, definition?: { default_scale: number }) {
    const scale = row.scale ?? definition?.default_scale ?? 1;
    if (!row.placed && definition) {
      return definition.default_scale;
    }
    if (this.usesLegacyPlacement(row)) {
      return definition?.default_scale ?? scale;
    }
    if (this.isBedDecor(row.decor_id) && scale <= ROOM_DECOR_LEGACY_BED_MAX_SCALE) {
      return ROOM_DECOR_BED_DEFAULT_SCALE;
    }
    if (row.decor_id === 'whale_floor_rug' && scale >= 0.22 && scale <= 0.30) {
      return definition?.default_scale ?? scale;
    }
    if (row.decor_id === 'dev_whale_bookshelf' && (Math.abs(scale - 0.4) <= 0.001 || (scale >= 0.51 && scale <= 0.53))) {
      // Preserve the old room-fit footprint after switching to the larger mall texture.
      return definition?.default_scale ?? scale;
    }
    if (this.isWallBadgeDecor(row.decor_id) && this.isLegacyWallDecorScale(scale)) {
      return definition?.default_scale ?? scale;
    }
    if (row.decor_id === 'whale_tail_lamp' && scale <= 0.2) {
      return definition?.default_scale ?? scale;
    }
    return scale;
  }

  private normalizedPositionValue(value: number | null, fallback: number, usesLegacyPlacement: boolean) {
    if (value === null || value === undefined) {
      return fallback;
    }
    return usesLegacyPlacement ? Math.round(value * ROOM_DECOR_ROOM_SCALE) : value;
  }

  private usesLegacyPlacement(row: UserRoomDecorRow) {
    const legacy = ROOM_DECOR_LEGACY_DEFAULTS[row.decor_id];
    if (!legacy) {
      return false;
    }
    const scale = row.scale ?? legacy.scale;
    if (this.isBedDecor(row.decor_id) && scale <= ROOM_DECOR_LEGACY_BED_MAX_SCALE) {
      return true;
    }
    return Math.abs(scale - legacy.scale) <= 0.001;
  }

  private isBedDecor(decorId: string) {
    return decorId.endsWith('_bed');
  }

  private isWallBadgeDecor(decorId: string) {
    return decorId.startsWith('datawhale_') && decorId.endsWith('_badge');
  }

  private isLegacyWallDecorScale(scale: number) {
    return ROOM_DECOR_LEGACY_WALL_DECOR_SCALES.some((legacyScale) => Math.abs(scale - legacyScale) <= 0.001);
  }
}
