import { useGameStore } from '@/store';
import { formatMoney, formatPercent, formatNumber, FUNDING_ROUNDS } from '@ai-tycoon/shared';
import type { FundingRoundType } from '@ai-tycoon/shared';
import { TrendingUp, DollarSign, PiggyBank, BarChart3, Rocket, Check, X as XIcon } from 'lucide-react';
import { AreaChart, Area, XAxis, YAxis, ResponsiveContainer, LineChart, Line, Tooltip } from 'recharts';
import { canRaiseFunding } from '@ai-tycoon/game-engine';
import type { GameState } from '@ai-tycoon/shared';
export function FinancePage() {
const money = useGameStore((s) => s.economy.money);
const revenuePerTick = useGameStore((s) => s.economy.revenuePerTick);
const expensesPerTick = useGameStore((s) => s.economy.expensesPerTick);
const funding = useGameStore((s) => s.economy.funding);
const history = useGameStore((s) => s.economy.financialHistory);
const infrastructure = useGameStore((s) => s.infrastructure);
const talent = useGameStore((s) => s.talent);
const raiseFunding = useGameStore((s) => s.raiseFunding);
const totalRevenue = useGameStore((s) => s.economy.totalRevenue);
const subscribers = useGameStore((s) => s.market.consumerTiers.totalUsers);
const reputationScore = useGameStore((s) => s.reputation.score);
const state = useGameStore.getState();
const gameStateForFunding: GameState = {
meta: state.meta, economy: state.economy, infrastructure: state.infrastructure,
compute: state.compute, research: state.research, models: state.models,
market: state.market, competitors: state.competitors, talent: state.talent,
data: state.data, reputation: state.reputation,
achievements: state.achievements,
};
const fundingStatus = canRaiseFunding(gameStateForFunding);
const netIncome = revenuePerTick - expensesPerTick;
const burnRate = expensesPerTick > revenuePerTick ? expensesPerTick - revenuePerTick : 0;
const runway = burnRate > 0 ? money / burnRate : Infinity;
let infraCosts = 0;
for (const cluster of infrastructure.clusters) {
for (const campus of cluster.campuses) {
for (const dc of campus.dataCenters) {
infraCosts += dc.energyCostPerTick + dc.maintenanceCostPerTick;
}
}
}
const talentCosts = talent.totalSalaryPerTick;
return (
Finance
= 0 ? 'text-green-400' : 'text-red-400'}
/>
Cash Over Time
{history.length > 1 ? (
formatMoney(v)} tick={{ fontSize: 10, fill: '#64748b' }} axisLine={false} tickLine={false} />
) : (
Data will appear as time passes
)}
Revenue vs Expenses
Revenue
Expenses
{history.length > 1 ? (
formatMoney(v)} tick={{ fontSize: 10, fill: '#64748b' }} axisLine={false} tickLine={false} />
[formatMoney(value), name === 'revenue' ? 'Revenue' : 'Expenses']}
/>
) : (
Data will appear as time passes
)}
Income Statement (per second)
Revenue
{formatMoney(revenuePerTick)}
Infrastructure
-{formatMoney(infraCosts)}
Talent
-{formatMoney(talentCosts)}
Total Expenses
-{formatMoney(expensesPerTick)}
Net Income
= 0 ? 'text-success' : 'text-danger'}`}>
{formatMoney(netIncome)}
Funding
Founder Equity: {formatPercent(funding.founderEquity)}
Total Raised: {formatMoney(funding.totalRaised)}
{funding.isPublic && (
Public
)}
{fundingStatus.nextRound && (() => {
const roundConfig = FUNDING_ROUNDS[fundingStatus.nextRound as FundingRoundType];
const reqs = roundConfig.requirements;
const checks = [
...(reqs.minRevenue ? [{ label: `Total Revenue: ${formatMoney(totalRevenue)} / ${formatMoney(reqs.minRevenue)}`, met: totalRevenue >= reqs.minRevenue }] : []),
...(reqs.minUsers ? [{ label: `Subscribers: ${formatNumber(subscribers)} / ${formatNumber(reqs.minUsers)}`, met: subscribers >= reqs.minUsers }] : []),
...(reqs.minReputation ? [{ label: `Reputation: ${reputationScore} / ${reqs.minReputation}`, met: reputationScore >= reqs.minReputation }] : []),
];
return (
{fundingStatus.nextRound === 'ipo' ? 'IPO' : fundingStatus.nextRound.replace('series', 'Series ')}
{fundingStatus.canRaise && (
)}
{checks.length > 0 && (
{checks.map((c, i) => (
{c.met ? : }
{c.label}
))}
)}
);
})()}
{funding.completedRounds.length === 0 ? (
No funding rounds completed yet.
) : (
{funding.completedRounds.map((round, i) => (
{round.type === 'ipo' ? 'IPO' : round.type.replace('series', 'Series ')}
{formatMoney(round.amount)}
{formatPercent(round.dilution)} dilution
))}
)}
);
}
function FinanceCard({ icon: Icon, label, value, color }: {
icon: typeof DollarSign;
label: string;
value: string;
color: string;
}) {
return (
);
}