/**
 * MarketManager.js
 * Manages the Hub Marketplace - stock generation, transactions, and persistence.
 */

/**
 * @typedef {Object} MarketItem
 * @property {string} id - Unique Stock ID (e.g. "STOCK_001")
 * @property {string} defId - Reference to ItemRegistry (e.g. "ITEM_RUSTY_BLADE")
 * @property {string} type - ItemType (cached for filtering)
 * @property {string} rarity - Rarity (cached for sorting/styling)
 * @property {number} price - Purchase price
 * @property {number} discount - 0.0 to 1.0 (percent off)
 * @property {boolean} purchased - If true, show as "Sold Out"
 * @property {Object} [instanceData] - If this is a buyback, store the ItemInstance here
 */

/**
 * @typedef {Object} MarketState
 * @property {string} generationId - Timestamp or Mission Count when this stock was generated
 * @property {MarketItem[]} stock - The active inventory for sale
 * @property {MarketItem[]} buyback - Items sold by the player this session (can be bought back)
 * @property {string} [specialOffer] - ID of a specific item (Daily Deal)
 */

/**
 * @typedef {Object} StockTable
 * @property {number} minItems - Minimum items to generate
 * @property {number} maxItems - Maximum items to generate
 * @property {Object} rarityWeights - Rarity distribution weights
 * @property {string[]} allowedTypes - Item types this merchant can sell
 */

export class MarketManager {
  /**
   * @param {import("../core/Persistence.js").Persistence} persistence - Persistence manager
   * @param {import("../managers/ItemRegistry.js").ItemRegistry} itemRegistry - Item registry
   * @param {import("../managers/InventoryManager.js").InventoryManager} inventoryManager - Inventory manager (for hubStash access)
   * @param {import("../managers/MissionManager.js").MissionManager} [missionManager] - Mission manager (optional, for tier calculation)
   */
  constructor(
    persistence,
    itemRegistry,
    inventoryManager,
    missionManager = null
  ) {
    /** @type {import("../core/Persistence.js").Persistence} */
    this.persistence = persistence;
    /** @type {import("../managers/ItemRegistry.js").ItemRegistry} */
    this.itemRegistry = itemRegistry;
    /** @type {import("../managers/InventoryManager.js").InventoryManager} */
    this.inventoryManager = inventoryManager;
    /** @type {import("../managers/MissionManager.js").MissionManager | null} */
    this.missionManager = missionManager;

    /** @type {MarketState | null} */
    this.marketState = null;

    /** @type {boolean} */
    this.needsRefresh = false;

    // Listen for mission completion
    this._boundHandleMissionVictory = this._handleMissionVictory.bind(this);
    window.addEventListener("mission-victory", this._boundHandleMissionVictory);
  }

  /**
   * Initializes the market manager and loads state from IndexedDB.
   * @returns {Promise<void>}
   */
  async init() {
    await this.itemRegistry.loadAll();
    const savedState = await this.persistence.loadMarketState();
    if (savedState) {
      this.marketState = savedState;
    } else {
      // Generate initial Tier 1 stock
      this.marketState = {
        generationId: `INIT_${Date.now()}`,
        stock: [],
        buyback: [],
      };
      await this.generateStock(1);
    }
  }

  /**
   * Handles mission victory event to set refresh flag.
   * @private
   */
  _handleMissionVictory() {
    this.needsRefresh = true;
  }

  /**
   * Checks if refresh is needed and generates new stock if entering hub.
   * Should be called when player enters Hub.
   * @returns {Promise<void>}
   */
  async checkRefresh() {
    if (this.needsRefresh) {
      // Determine tier based on completed missions count
      // For now, use Tier 1 for < 3 missions, Tier 2 for >= 3
      const completedCount = this._getCompletedMissionCount();
      const tier = completedCount < 3 ? 1 : 2;
      await this.generateStock(tier);
      this.needsRefresh = false;
    }
  }

  /**
   * Gets the number of completed missions.
   * @returns {number}
   * @private
   */
  _getCompletedMissionCount() {
    if (this.missionManager) {
      return this.missionManager.completedMissions.size;
    }
    return 0;
  }

  /**
   * Generates new stock based on tier.
   * @param {number} tier - Game tier (1 = Early, 2 = Mid)
   * @returns {Promise<void>}
   */
  async generateStock(tier) {
    const allItems = this.itemRegistry.getAll();
    const newStock = [];

    if (tier === 1) {
      // Tier 1: Smith (5 Common Weapons, 3 Common Armor)
      const smithWeapons = this._generateMerchantStock(
        allItems,
        ["WEAPON"],
        { COMMON: 1, UNCOMMON: 0, RARE: 0, ANCIENT: 0 },
        5
      );
      const smithArmor = this._generateMerchantStock(
        allItems,
        ["ARMOR"],
        { COMMON: 1, UNCOMMON: 0, RARE: 0, ANCIENT: 0 },
        3
      );
      newStock.push(...smithWeapons, ...smithArmor);

      // Alchemist (5 Potions, 2 Grenades) - simplified for now since we don't have potions in tier1_gear
      // Will add when consumables are available
    } else if (tier === 2) {
      // Tier 2: Weights Common (60%), Uncommon (30%), Rare (10%)
      const tier2Weights = {
        COMMON: 0.6,
        UNCOMMON: 0.3,
        RARE: 0.1,
        ANCIENT: 0,
      };

      // Generate mixed stock
      const weapons = this._generateMerchantStock(
        allItems,
        ["WEAPON"],
        tier2Weights,
        8
      );
      const armor = this._generateMerchantStock(
        allItems,
        ["ARMOR"],
        tier2Weights,
        5
      );
      const utility = this._generateMerchantStock(
        allItems,
        ["UTILITY"],
        tier2Weights,
        3
      );
      newStock.push(...weapons, ...armor, ...utility);
    }

    // Assign stock IDs and prices
    const stockWithIds = newStock.map((item, index) => {
      const itemDef = this.itemRegistry.get(item.defId);
      const basePrice = this._calculateBasePrice(itemDef);
      const variance = 1 + (Math.random() * 0.2 - 0.1); // ±10% variance
      const price = Math.floor(basePrice * variance);

      return {
        id: `STOCK_${Date.now()}_${index}`,
        defId: item.defId,
        type: item.type,
        rarity: item.rarity,
        price: price,
        discount: 0,
        purchased: false,
      };
    });

    this.marketState.stock = stockWithIds;
    this.marketState.generationId = `TIER${tier}_${Date.now()}`;
    await this.persistence.saveMarketState(this.marketState);
  }

  /**
   * Generates stock for a specific merchant type.
   * @param {import("../items/Item.js").Item[]} allItems - All available items
   * @param {string[]} allowedTypes - Item types to filter
   * @param {Object} rarityWeights - Rarity weight distribution
   * @param {number} count - Number of items to generate
   * @returns {Array<{defId: string, type: string, rarity: string}>}
   * @private
   */
  _generateMerchantStock(allItems, allowedTypes, rarityWeights, count) {
    // Filter by allowed types
    const filtered = allItems.filter((item) =>
      allowedTypes.includes(item.type)
    );

    if (filtered.length === 0) return [];

    const result = [];
    for (let i = 0; i < count; i++) {
      // Roll for rarity
      const roll = Math.random();
      let selectedRarity = "COMMON";
      let cumulative = 0;
      for (const [rarity, weight] of Object.entries(rarityWeights)) {
        cumulative += weight;
        if (roll <= cumulative) {
          selectedRarity = rarity;
          break;
        }
      }

      // Filter by selected rarity
      const rarityFiltered = filtered.filter(
        (item) => item.rarity === selectedRarity
      );

      if (rarityFiltered.length === 0) {
        // Fallback to any rarity if none found
        const randomItem =
          filtered[Math.floor(Math.random() * filtered.length)];
        result.push({
          defId: randomItem.id,
          type: randomItem.type,
          rarity: randomItem.rarity,
        });
      } else {
        const randomItem =
          rarityFiltered[Math.floor(Math.random() * rarityFiltered.length)];
        result.push({
          defId: randomItem.id,
          type: randomItem.type,
          rarity: randomItem.rarity,
        });
      }
    }

    return result;
  }

  /**
   * Calculates base price for an item based on its stats and rarity.
   * @param {import("../items/Item.js").Item} itemDef - Item definition
   * @returns {number}
   * @private
   */
  _calculateBasePrice(itemDef) {
    // Base price calculation based on stats and rarity
    let basePrice = 50; // Base value

    // Add stat values
    const stats = itemDef.stats || {};
    const statValue = Object.values(stats).reduce((sum, val) => sum + val, 0);
    basePrice += statValue * 10;

    // Rarity multiplier
    const rarityMultipliers = {
      COMMON: 1,
      UNCOMMON: 1.5,
      RARE: 2.5,
      ANCIENT: 5,
    };
    basePrice *= rarityMultipliers[itemDef.rarity] || 1;

    return Math.floor(basePrice);
  }

  /**
   * Buys an item from the market.
   * Atomic transaction: checks currency, deducts, creates instance, adds to stash, marks purchased, saves.
   * @param {string} stockId - Stock ID of the item to buy
   * @returns {Promise<boolean>} - True if successful, false otherwise
   */
  async buyItem(stockId) {
    // Check both stock and buyback
    let marketItem = this.marketState.stock.find((item) => item.id === stockId);
    if (!marketItem) {
      marketItem = this.marketState.buyback.find((item) => item.id === stockId);
    }
    if (!marketItem || marketItem.purchased) {
      return false;
    }

    // Get wallet from hub stash
    const wallet = this.inventoryManager.hubStash.currency;
    if (wallet.aetherShards < marketItem.price) {
      return false;
    }

    // Atomic transaction
    try {
      // 1. Deduct currency
      wallet.aetherShards -= marketItem.price;

      // 2. Generate ItemInstance (or restore from buyback)
      let itemInstance;
      if (marketItem.instanceData) {
        // Restore original instance from buyback
        itemInstance = marketItem.instanceData;
      } else {
        // Create new instance
        itemInstance = {
          uid: `ITEM_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
          defId: marketItem.defId,
          isNew: true,
          quantity: 1,
        };
      }

      // 3. Add to hubStash
      this.inventoryManager.hubStash.addItem(itemInstance);

      // 4. Mark as purchased
      marketItem.purchased = true;

      // 5. Save market state
      await this.persistence.saveMarketState(this.marketState);

      // 6. Save hub stash to localStorage (via GameStateManager)
      // Import GameStateManager to access _saveHubStash
      const { gameStateManager } = await import("../core/GameStateManager.js");
      await gameStateManager._saveHubStash();

      // 7. Dispatch wallet-updated event to refresh UI
      window.dispatchEvent(
        new CustomEvent("wallet-updated", {
          bubbles: true,
          composed: true,
        })
      );

      return true;
    } catch (error) {
      console.error("Error buying item:", error);
      // Rollback would go here in a production system
      return false;
    }
  }

  /**
   * Sells an item to the market.
   * Atomic transaction: removes from stash, calculates value, adds currency, creates buyback, saves.
   * @param {string} itemUid - UID of the item instance to sell
   * @returns {Promise<boolean>} - True if successful, false otherwise
   */
  async sellItem(itemUid) {
    // Find item in hubStash
    const itemInstance = this.inventoryManager.hubStash.findItem(itemUid);
    if (!itemInstance) {
      return false;
    }

    // Get item definition
    const itemDef = this.itemRegistry.get(itemInstance.defId);
    if (!itemDef) {
      return false;
    }

    // Atomic transaction
    try {
      // 1. Remove from hubStash
      this.inventoryManager.hubStash.removeItem(itemUid);

      // 2. Calculate sell price (25% of base price)
      const basePrice = this._calculateBasePrice(itemDef);
      const sellPrice = Math.floor(basePrice * 0.25);

      // 3. Add currency
      this.inventoryManager.hubStash.currency.aetherShards += sellPrice;

      // 4. Save hub stash to localStorage (via GameStateManager)
      const { gameStateManager } = await import("../core/GameStateManager.js");
      await gameStateManager._saveHubStash();

      // 5. Dispatch wallet-updated event to refresh UI
      window.dispatchEvent(
        new CustomEvent("wallet-updated", {
          bubbles: true,
          composed: true,
        })
      );

      // 6. Create buyback entry (limit 10)
      if (this.marketState.buyback.length >= 10) {
        this.marketState.buyback.shift(); // Remove oldest
      }

      const buybackItem = {
        id: `BUYBACK_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        defId: itemInstance.defId,
        type: itemDef.type,
        rarity: itemDef.rarity,
        price: sellPrice, // Buyback price = sell price
        discount: 0,
        purchased: false,
        instanceData: { ...itemInstance }, // Store copy of original instance
      };

      this.marketState.buyback.push(buybackItem);

      // 7. Save market state
      await this.persistence.saveMarketState(this.marketState);

      return true;
    } catch (error) {
      console.error("Error selling item:", error);
      return false;
    }
  }

  /**
   * Gets the current market state.
   * @returns {MarketState}
   */
  getState() {
    return this.marketState;
  }

  /**
   * Gets stock filtered by merchant type.
   * @param {string} merchantType - "SMITH" | "TAILOR" | "ALCHEMIST" | "SCAVENGER" | "BUYBACK"
   * @returns {MarketItem[]}
   */
  getStockForMerchant(merchantType) {
    if (merchantType === "BUYBACK") {
      return this.marketState.buyback;
    }

    // Filter stock by merchant type
    const typeMap = {
      SMITH: ["WEAPON", "ARMOR"],
      TAILOR: ["ARMOR"],
      ALCHEMIST: ["CONSUMABLE", "UTILITY"],
      SCAVENGER: ["RELIC", "UTILITY"],
    };

    const allowedTypes = typeMap[merchantType] || [];
    return this.marketState.stock.filter((item) =>
      allowedTypes.includes(item.type)
    );
  }

  /**
   * Cleanup - remove event listeners.
   */
  destroy() {
    window.removeEventListener(
      "mission-victory",
      this._boundHandleMissionVictory
    );
  }
}
