import { computed } from 'vue' import type { Ref } from 'vue' import type { CraftingRecipe, ProfitResult, IngredientBreakdown, SortState } from '../types/crafting' import type { FilterState, VariantType } from '../types/filters' import { useAlbionPrices } from './useAlbionPrices' import { formatItemId } from '../utils/formatting' import { localProductionBonus, rrrFromBonus, isRrrExempt, FOCUS_LPB } from '../data/cityBonuses' function variantOf(outputItemId: string): VariantType { const id = outputItemId.replace(/@\d$/, '') // strip enchantment suffix if (id.endsWith('_AVALON')) return 'avalon' if (id.endsWith('_CRYSTAL')) return 'crystal' if (/_SET[123]$/.test(id)) return 'basic' if (/_(?:UNDEAD|HELL|MORGANA|KEEPER|ROYAL|FEY)$/.test(id)) return 'artifact' return 'basic' } const VARIANT_ORDER: Record = { basic: 0, artifact: 1, avalon: 2, crystal: 3 } function variantRank(outputItemId: string): number { return VARIANT_ORDER[variantOf(outputItemId)] } export function useCraftingProfit( recipes: CraftingRecipe[], filters: Ref, sortState: Ref ) { const { getPrice } = useAlbionPrices() const profitResults = computed(() => { const f = filters.value const city = f.city const results: ProfitResult[] = [] const nameLower = f.nameFilter.trim().toLowerCase() for (const recipe of recipes) { if (!f.tiers.has(recipe.tier)) continue if (f.enchantments !== null && !f.enchantments.has(recipe.enchantment)) continue if (f.variants !== null && !f.variants.has(variantOf(recipe.outputItemId))) continue const baseName = recipe.displayName.replace(/^T\d+\.\d /, '') if (f.selectedItemTypes !== null && !f.selectedItemTypes.has(baseName)) continue if (nameLower && !recipe.displayName.toLowerCase().includes(nameLower)) continue // Calculate material cost from ingredients only let basicCost = 0 // resources subject to RRR let artefactCost = 0 // artefacts / ALCHEMY_RARE — not subject to RRR let missingPrices = false // Track oldest price date (for status column) let oldestDate: string | null = null function trackDate(date: string) { if (!oldestDate || date < oldestDate) oldestDate = date } const ingredientBreakdown: IngredientBreakdown[] = [] for (const ing of recipe.ingredients) { const ingEntry = getPrice(ing.itemId, city) const unitPrice = ingEntry?.sell_price_min ?? 0 if (ingEntry === null || unitPrice === 0) { missingPrices = true ingredientBreakdown.push({ itemId: ing.itemId, displayName: formatItemId(ing.itemId), quantity: ing.quantity, unitPrice: 0, totalCost: 0, }) } else { trackDate(ingEntry.sell_price_min_date) const totalCost = unitPrice * ing.quantity if (isRrrExempt(ing.itemId)) artefactCost += totalCost else basicCost += totalCost ingredientBreakdown.push({ itemId: ing.itemId, displayName: formatItemId(ing.itemId), quantity: ing.quantity, unitPrice, totalCost, }) } } const materialCost = basicCost + artefactCost const lpb = localProductionBonus(city, recipe.outputItemId) const rrr = rrrFromBonus(lpb) const rrrFocus = rrrFromBonus(lpb + FOCUS_LPB) const effectiveMaterialCost = basicCost * (1 - rrr / 100) + artefactCost const effectiveMaterialCostFocus = basicCost * (1 - rrrFocus / 100) + artefactCost const priceAgeMs = missingPrices ? null : (oldestDate ? Date.now() - new Date(oldestDate).getTime() : null) results.push({ recipe, materialCost, effectiveMaterialCost, effectiveMaterialCostFocus, rrr, rrrFocus, priceAgeMs, missingPrices, ingredientBreakdown, }) } // Sort results const { field, direction } = sortState.value results.sort((a, b) => { let aVal: number | string let bVal: number | string switch (field) { case 'materialCost': aVal = a.effectiveMaterialCost bVal = b.effectiveMaterialCost break case 'displayName': aVal = a.recipe.displayName bVal = b.recipe.displayName break case 'tier': aVal = a.recipe.tier bVal = b.recipe.tier break case 'variantType': aVal = variantRank(a.recipe.outputItemId) bVal = variantRank(b.recipe.outputItemId) break default: aVal = a.effectiveMaterialCost bVal = b.effectiveMaterialCost } if (typeof aVal === 'string' && typeof bVal === 'string') { return direction === 'asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal) } const diff = (aVal as number) - (bVal as number) return direction === 'asc' ? diff : -diff }) return results }) return { profitResults } }