// Production UI: Individual Facility Card (HTML Mount and Fine-grained DOM update)
import { getProduct, getNextMilestone, getUnitMilestoneMultiplier, SECTORS } from '../../data/products.js';
import { fmtCurrency, fmtNum } from '../../core/formatters.js';
import { calculateMultiCost, getProductOutputRates, computeProductNetFlow } from './ratesCalculator.js';
export function renderFacilityCardHtml(p, pState, state, buyMode) {
const sector = SECTORS.find(s => s.id === p.sector);
const isSelected = state.selectedProductId === p.id;
if (!pState.unlocked) {
const canAffordUnlock = state.cash >= p.unlockCost;
return `
`;
}
const nextM = getNextMilestone(pState.lines);
const rates = getProductOutputRates(p, pState, state);
const milestoneMult = rates.milestoneMult || getUnitMilestoneMultiplier(pState.lines);
const calc = calculateMultiCost(p.baseFacilityCost, pState.lines, buyMode);
const canAffordBuy = state.cash >= calc.cost;
const managerCost = p.baseManagerCost;
const canAffordManager = state.cash >= managerCost;
const logisticsCost = p.baseLogisticsCost * Math.pow(1.15, pState.logistics || 0);
const canAffordLogistics = state.cash >= logisticsCost;
const isRaw = !p.inputs || p.inputs.length === 0;
let inputsHtml = '';
if (!isRaw) {
inputsHtml = `
`;
}
let statusBadge = '';
if (pState.isBottlenecked) {
statusBadge = ` Waiting for Inputs`;
} else if (pState.lines > 0) {
statusBadge = ` Operating (${milestoneMult}x Speed)`;
} else {
statusBadge = `Idle (0 Lines)`;
}
const progressPct = Math.min(100, Math.floor((pState.cycleProgress || 0) * 100));
return `
Stockpile:
${fmtNum(pState.stock, 0)} ${p.unit}
Production Output:
${rates.isBottlenecked ? '0.0 (Paused)' : `${fmtNum(rates.prodRate, 1)}/s`}
Net Balance:
+0.0/s
Logistics Rev:
+$${fmtNum(rates.revRate, 2)}/s
Wholesale Price:
${fmtCurrency(p.basePrice)}
Logistics Auto-Sell:
${pState.logistics || 0} fleets (${(pState.logisticsEnabled !== false) ? `${fmtNum((pState.logistics || 0) * 0.75, 2)}/s` : 'Paused'})
${inputsHtml}
Policy:
${!pState.manager ? `
` : `
Lead Active (Auto)
`}
`;
}
export function updateFacilityCardDom(p, pState, state, buyMode) {
const cash = state.cash;
if (!pState.unlocked) {
const btnUnlock = document.getElementById(`btn-unlock-${p.id}`);
if (btnUnlock) {
const canAfford = cash >= p.unlockCost;
if (btnUnlock.disabled !== !canAfford) {
btnUnlock.disabled = !canAfford;
}
}
return;
}
// Live Individual Factory Rates & Output
const rates = getProductOutputRates(p, pState, state);
const elRateUnits = document.getElementById(`rate-units-${p.id}`);
if (elRateUnits) {
const unitsText = rates.isBottlenecked ? '0.0 (Paused)' : `${fmtNum(rates.prodRate, 1)}/s`;
if (elRateUnits.textContent !== unitsText) {
elRateUnits.textContent = unitsText;
}
elRateUnits.className = `stat-value font-mono ${rates.isBottlenecked ? 'text-warning' : 'text-profit'}`;
}
// Live Net Flow (Production minus Downstream Consumption minus Logistics Sales)
const netFlow = computeProductNetFlow(p, state);
const elNetFlow = document.getElementById(`net-flow-${p.id}`);
if (elNetFlow) {
const sign = netFlow.netRate > 0.01 ? '+' : '';
const netText = `${sign}${fmtNum(netFlow.netRate, 1)}/s`;
if (elNetFlow.textContent !== netText) {
elNetFlow.textContent = netText;
}
const isDeficit = netFlow.netRate < -0.05;
const isSurplus = netFlow.netRate > 0.05;
elNetFlow.className = `stat-value font-mono ${isDeficit ? 'text-warning' : (isSurplus ? 'text-profit' : '')}`;
}
const elRateRev = document.getElementById(`rate-rev-${p.id}`);
if (elRateRev) {
const revText = `+$${fmtNum(rates.revRate, 2)}/s`;
if (elRateRev.textContent !== revText) {
elRateRev.textContent = revText;
}
}
const elEff = document.getElementById(`eff-${p.id}`);
if (elEff) {
const effText = `${rates.milestoneMult}x Speed`;
if (elEff.textContent !== effText) {
elEff.textContent = effText;
}
}
const elLogistics = document.getElementById(`logistics-${p.id}`);
if (elLogistics) {
const count = pState.logistics || 0;
const isLogActive = pState.logisticsEnabled !== false;
const ratePart = isLogActive ? `${fmtNum(count * 0.75, 2)}/s` : 'Paused';
const logText = `${count} fleets (${ratePart})`;
if (elLogistics.textContent !== logText) {
elLogistics.textContent = logText;
}
}
// Policy Button Toggles DOM State
const btnToggleLog = document.getElementById(`btn-toggle-logistics-${p.id}`);
if (btnToggleLog) {
const isLogActive = pState.logisticsEnabled !== false;
btnToggleLog.className = `btn-policy-toggle ${isLogActive ? 'active' : 'inactive'} btn-toggle-logistics`;
const textSpan = document.getElementById(`btn-toggle-logistics-text-${p.id}`);
if (textSpan) {
const tText = `Wholesale Fleet: ${isLogActive ? 'ON' : 'OFF'}`;
if (textSpan.textContent !== tText) textSpan.textContent = tText;
}
const iconEl = btnToggleLog.querySelector('iconify-icon');
if (iconEl) {
iconEl.setAttribute('icon', isLogActive ? 'lucide:truck' : 'lucide:truck-x');
}
}
const btnToggleDown = document.getElementById(`btn-toggle-downstream-${p.id}`);
if (btnToggleDown) {
const isDownActive = pState.supplyDownstreamEnabled !== false;
btnToggleDown.className = `btn-policy-toggle ${isDownActive ? 'active' : 'inactive'} btn-toggle-downstream`;
const textSpan = document.getElementById(`btn-toggle-downstream-text-${p.id}`);
if (textSpan) {
const tText = `Downstream Supply: ${isDownActive ? 'ON' : 'OFF'}`;
if (textSpan.textContent !== tText) textSpan.textContent = tText;
}
const iconEl = btnToggleDown.querySelector('iconify-icon');
if (iconEl) {
iconEl.setAttribute('icon', isDownActive ? 'lucide:git-pull-request' : 'lucide:git-pull-request-closed');
}
}
const elProgress = document.getElementById(`progress-${p.id}`);
if (elProgress) {
const progressPct = Math.min(100, Math.floor((pState.cycleProgress || 0) * 100));
elProgress.style.width = `${progressPct}%`;
}
// Stockpile Text
const elStock = document.getElementById(`stock-${p.id}`);
if (elStock) {
const stockText = fmtNum(pState.stock, 0);
if (elStock.textContent !== stockText) {
elStock.textContent = stockText;
}
}
// Status Badge
const elBadge = document.getElementById(`badge-${p.id}`);
if (elBadge) {
const isBottlenecked = pState.isBottlenecked;
const hasWarning = elBadge.classList.contains('badge-warning');
if (isBottlenecked && !hasWarning) {
elBadge.className = 'status-badge badge-warning';
elBadge.innerHTML = ` Waiting for Inputs`;
} else if (!isBottlenecked && hasWarning) {
const milestoneMult = getUnitMilestoneMultiplier(pState.lines);
elBadge.className = 'status-badge badge-success';
elBadge.innerHTML = ` Operating (${milestoneMult}x Speed)`;
}
}
// Input Chips Stock
if (p.inputs && p.inputs.length > 0) {
for (const inp of p.inputs) {
const currentStock = state.products[inp.id]?.stock || 0;
const elChipVal = document.getElementById(`chip-val-${p.id}-${inp.id}`);
if (elChipVal) {
const textVal = fmtNum(currentStock, 0);
if (elChipVal.textContent !== textVal) {
elChipVal.textContent = textVal;
}
}
const elChip = document.getElementById(`chip-${p.id}-${inp.id}`);
if (elChip) {
const needed = inp.amount * pState.lines;
const isLow = currentStock < needed;
elChip.classList.toggle('low-stock', isLow);
}
}
}
// Expand Button disabled state
const calc = calculateMultiCost(p.baseFacilityCost, pState.lines, buyMode);
const elBtnExpand = document.getElementById(`btn-expand-${p.id}`);
if (elBtnExpand) {
const canAfford = cash >= calc.cost;
if (elBtnExpand.disabled !== !canAfford) {
elBtnExpand.disabled = !canAfford;
}
}
// Hire Manager disabled state
const elBtnHire = document.getElementById(`btn-hire-${p.id}`);
if (elBtnHire) {
const canAfford = cash >= p.baseManagerCost;
if (elBtnHire.disabled !== !canAfford) {
elBtnHire.disabled = !canAfford;
}
}
// Logistics Button disabled state
const logisticsCost = p.baseLogisticsCost * Math.pow(1.15, pState.logistics || 0);
const elBtnLogistics = document.getElementById(`btn-logistics-${p.id}`);
if (elBtnLogistics) {
const canAfford = cash >= logisticsCost;
if (elBtnLogistics.disabled !== !canAfford) {
elBtnLogistics.disabled = !canAfford;
}
const elBtnLogisticsText = document.getElementById(`btn-logistics-text-${p.id}`);
if (elBtnLogisticsText) {
const text = `+Fleet (${fmtCurrency(logisticsCost)})`;
if (elBtnLogisticsText.textContent !== text) {
elBtnLogisticsText.textContent = text;
}
}
}
// Selected Card class
const elCard = document.getElementById(`card-${p.id}`);
if (elCard) {
const isSelected = state.selectedProductId === p.id;
elCard.classList.toggle('selected', isSelected);
}
}