import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
import { MALL_CATEGORIES, MALL_ITEMS, findMallItem } from './mall_catalog';
import { InventoryService } from '../player/inventory.service';
import { EconomyService } from '../player/economy.service';
import { PlayerStateService } from '../player/player_state.service';
import { PlayerInventoryPayload, PlayerSnapshotPayload, PlayerWalletPayload } from '../player/player.types';
import { TaskService } from '../tasks/task.service';

interface IUserWalletsService {
  getBalance(userId: bigint): Promise<{ balance: number; currency: 'whale_coin'; user_id: string }>;
}

export interface PurchaseMallItemResult {
  item_id: string;
  item_type: string;
  skin_id?: string;
  decor_id?: string;
  price: number;
  balance: number;
  currency: 'whale_coin';
  owned_skin_ids: string[];
  owned_decor_ids: string[];
  already_owned: boolean;
  wallet: PlayerWalletPayload;
  inventory: PlayerInventoryPayload;
  snapshot: PlayerSnapshotPayload;
}

export interface MallCatalogItemPayload {
  id: string;
  itemType: string;
  skinId?: string;
  decorId?: string;
  icon?: string;
  name: string;
  category: string;
  description: string;
  price: number;
  status: 'owned' | 'available';
  tags: string[];
  sortOrder: number;
}

export interface MallCatalogPayload {
  balance: number;
  currency: 'whale_coin';
  categories: Array<{ id: string; label: string; icon: string }>;
  items: MallCatalogItemPayload[];
  owned_skin_ids: string[];
  owned_decor_ids: string[];
}

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

  constructor(
    @Inject('IUserWalletsService') private readonly userWalletsService: IUserWalletsService,
    private readonly inventoryService: InventoryService,
    private readonly economyService: EconomyService,
    private readonly playerStateService: PlayerStateService,
    private readonly taskService: TaskService,
  ) {}

  async getWallet(userId: bigint) {
    return await this.userWalletsService.getBalance(userId);
  }

  async getCatalog(userId: bigint): Promise<MallCatalogPayload> {
    const [wallet, inventory] = await Promise.all([
      this.userWalletsService.getBalance(userId),
      this.inventoryService.listInventory(userId),
    ]);
    const ownedSkinIds = inventory.skin_ids;
    const ownedDecorIds = inventory.room_decor_ids;
    const ownedSet = new Set(ownedSkinIds);
    const ownedDecorSet = new Set(ownedDecorIds);
    const items = MALL_ITEMS
      .map((item) => ({
        id: item.itemId,
        itemType: item.itemType,
        skinId: item.skinId,
        decorId: item.decorId,
        icon: item.icon,
        name: item.name,
        category: item.category,
        description: item.description,
        price: item.price,
        status: (
          (item.skinId && ownedSet.has(item.skinId)) ||
          (item.decorId && ownedDecorSet.has(item.decorId))
        ) ? 'owned' as const : 'available' as const,
        tags: item.tags,
        sortOrder: item.sortOrder,
      }))
      .sort((a, b) => a.sortOrder - b.sortOrder);

    return {
      balance: wallet.balance,
      currency: wallet.currency,
      categories: MALL_CATEGORIES,
      items,
      owned_skin_ids: ownedSkinIds,
      owned_decor_ids: ownedDecorIds,
    };
  }

  async purchaseItem(userId: bigint, itemId: string): Promise<PurchaseMallItemResult> {
    const item = findMallItem(itemId);
    if (!item) {
      throw new BadRequestException('商品不存在或暂未开放');
    }
    if (item.itemType === 'skin' && item.skinId) {
      return await this.purchaseSkinItem(userId, item);
    }
    if (item.itemType === 'room_decor' && item.decorId) {
      return await this.purchaseRoomDecorItem(userId, item);
    }
    throw new BadRequestException('商品类型暂未开放');
  }

  private async purchaseSkinItem(userId: bigint, item: NonNullable<ReturnType<typeof findMallItem>>): Promise<PurchaseMallItemResult> {
    const alreadyOwned = await this.inventoryService.hasAsset(userId, 'skin', item.skinId as string);
    let wallet = await this.economyService.getWallet(userId);
    if (!alreadyOwned && item.price > 0) {
      wallet = await this.economyService.spend(userId, item.price, 'shop_purchase', item.itemId, `购买皮肤：${item.name}`);
    }

    await this.inventoryService.grantAsset(userId, 'skin', item.skinId as string, 'purchase');
    if (!alreadyOwned) {
      await this.taskService.recordActivity(userId, 'skin_purchased', item.itemId)
        .catch((error: unknown) => this.logger.warn(`记录首次皮肤任务失败: ${error instanceof Error ? error.message : String(error)}`));
    }
    const [inventory, snapshot] = await Promise.all([
      this.inventoryService.listInventory(userId),
      this.playerStateService.getSnapshot(userId),
    ]);

    return {
      item_id: item.itemId,
      item_type: item.itemType,
      skin_id: item.skinId,
      price: item.price,
      balance: wallet.balance,
      currency: wallet.currency,
      owned_skin_ids: inventory.skin_ids,
      owned_decor_ids: inventory.room_decor_ids,
      already_owned: alreadyOwned,
      wallet,
      inventory,
      snapshot,
    };
  }

  private async purchaseRoomDecorItem(userId: bigint, item: NonNullable<ReturnType<typeof findMallItem>>): Promise<PurchaseMallItemResult> {
    const decorId = item.decorId as string;
    const alreadyOwned = await this.inventoryService.hasAsset(userId, 'room_decor', decorId);
    let wallet = await this.economyService.getWallet(userId);
    if (!alreadyOwned && item.price > 0) {
      wallet = await this.economyService.spend(userId, item.price, 'shop_purchase', item.itemId, `购买房间摆件：${item.name}`);
    }

    await this.inventoryService.grantAsset(userId, 'room_decor', decorId, 'purchase');
    const [inventory, snapshot] = await Promise.all([
      this.inventoryService.listInventory(userId),
      this.playerStateService.getSnapshot(userId),
    ]);

    return {
      item_id: item.itemId,
      item_type: item.itemType,
      decor_id: decorId,
      price: item.price,
      balance: wallet.balance,
      currency: wallet.currency,
      owned_skin_ids: inventory.skin_ids,
      owned_decor_ids: inventory.room_decor_ids,
      already_owned: alreadyOwned,
      wallet,
      inventory,
      snapshot,
    };
  }
}
