import Charts from "~/components/simulator/Charts";
import { useMemo, useCallback } from "react";
import type {
  Position,
  AssetPriceData,
  AssetPosition,
  WalletStats,
} from "~/interfaces/simulator";
import { Grid, Typography, Box } from "@mui/joy";
import InfoOutlined from "@mui/icons-material/InfoOutlined";
import useLocalization from "~/utils/useLocalization";

type Dataset = {
  label: string;
  fill: {
    target: string;
    above: string;
    below: string;
  };
  data: number[];
};

function computeWalletStats(
  positions: Position[],
  priceData: AssetPriceData[]
) {
  const assetPositions: AssetPosition = {};
  if (priceData.length === 0) return null;
  let globalFirstTs = 0;
  let globalLastTs = 0;

  for (let i = 0; i < positions.length; i++) {
    const ts = Date.parse(positions[i]!.date) / 1000;

    if (globalFirstTs === 0) globalFirstTs = ts;
    if (globalLastTs === 0) globalLastTs = ts;
    if (ts < globalFirstTs) globalFirstTs = ts;
    if (ts > globalLastTs) globalLastTs = ts;

    if (assetPositions[positions[i]!.ticker] === undefined) {
      assetPositions[positions[i]!.ticker] = [];
      assetPositions[positions[i]!.ticker]!.push({ ...positions[i]!, ts: ts });
    } else {
      assetPositions[positions[i]!.ticker]!.push({ ...positions[i]!, ts: ts });
    }
  }
  // ...

  // I need the asset value every day anyway
  // find a way to loop through every day from min to max
  // then have the wallet value total, asset value, etc
  // maybe fewer datapoints? imagine 10 years of positions... that's a lot, 1000's of datapoints
  // I can just slice the price data of the asset for daily data!
  let totalWalletValue = 0;
  let totalInvested = 0;

  const chart: { labels: number[]; datasets: Dataset[] } = {
    labels: [],
    datasets: [],
  };

  const totalInvestedTimeline = new Map<number, number>();

  const totalWalletValueTimeline = new Map<number, number>();

  const walletPerformanceTimeline = new Map<number, number>();

  for (let i = 0; i < priceData.length; i++) {
    const thisAssetPriceData = priceData[i];
    if (thisAssetPriceData !== undefined) {
      if (thisAssetPriceData.ticker === null) continue;
      const thisAssetId = thisAssetPriceData.ticker;
      const thisAssetPositions = assetPositions[thisAssetId];

      let totalAssetQuantity = 0;
      let totalValueInvested = 0;
      let medianPrice = 0;
      const assetPerformance: number[] = [];
      for (let j = 0; j < thisAssetPriceData.data.length; j++) {
        if (thisAssetPositions !== undefined && thisAssetPositions.length > 0) {
          const thisPriceData = thisAssetPriceData.data[j];
          if (thisPriceData !== undefined) {
            if (thisPriceData.date < globalFirstTs) {
              continue;
            }

            let valueInvested = 0;
            for (let k = 0; k < thisAssetPositions.length; k++) {
              if (thisAssetPositions[k]!.ts === thisPriceData.date) {
                valueInvested += parseInt(thisAssetPositions[k]!.qtd);
              }
            }
            if (valueInvested > 0) {
              // total invested divided by the price = amount bought
              const assetQuantity = valueInvested / thisPriceData.close;
              if (medianPrice == 0) {
                medianPrice = thisPriceData.close;
              } else {
                medianPrice = (medianPrice + thisPriceData.close) / 2;
              }

              totalAssetQuantity += assetQuantity;
              totalValueInvested += valueInvested;
            }

            if (totalValueInvested > 0) {
              // this asset performance timeline
              const performance =
                ((thisPriceData.close - medianPrice) / medianPrice) * 100;
              assetPerformance.push(performance);

              // total invested timeline
              if (totalInvestedTimeline.has(thisPriceData.date)) {
                const currentTotalInvested =
                  totalInvestedTimeline.get(thisPriceData.date) ?? 0;
                const newTotalInvested =
                  totalValueInvested + currentTotalInvested;
                totalInvestedTimeline.set(thisPriceData.date, newTotalInvested);
              } else {
                totalInvestedTimeline.set(
                  thisPriceData.date,
                  totalValueInvested
                );
              }

              // total wallet value timeline
              if (totalWalletValueTimeline.has(thisPriceData.date)) {
                const currentWalletValue =
                  totalWalletValueTimeline.get(thisPriceData.date) ?? 0;
                const newWalletValue =
                  currentWalletValue + thisPriceData.close * totalAssetQuantity;
                totalWalletValueTimeline.set(
                  thisPriceData.date,
                  newWalletValue
                );
              } else {
                totalWalletValueTimeline.set(
                  thisPriceData.date,
                  thisPriceData.close * totalAssetQuantity
                );
              }

              // wallet performance timeline

              //const [currentTotalInvested, currentWalletValue] = walletPerformanceTimeline.get(thisPriceData.date);
              const currentTotalInvested =
                totalInvestedTimeline.get(thisPriceData.date) ?? 0;
              const currentWalletValue =
                totalWalletValueTimeline.get(thisPriceData.date) ?? 0;
              const walletPerformance =
                ((currentWalletValue - currentTotalInvested) /
                  currentTotalInvested) *
                100;
              walletPerformanceTimeline.set(
                thisPriceData.date,
                walletPerformance
              );
            }
          }
        }
      }

      const lastAssetPrice =
        thisAssetPriceData.data[thisAssetPriceData.data.length - 1]?.close ?? 0;
      const totalAssetValue = totalAssetQuantity * lastAssetPrice;
      totalInvested += totalValueInvested;
      totalWalletValue += totalAssetValue;
    }
  }

  const sortedTimestamps = Array.from(walletPerformanceTimeline.keys()).sort();
  const finalPerformanceTimeline = sortedTimestamps.map(
    (ts) => walletPerformanceTimeline.get(ts) ?? 0
  );

  chart.labels = sortedTimestamps.map((ts) => ts * 1000);
  chart.datasets.push({
    label: "Carteira",
    fill: {
      target: "origin",
      above: "rgba(50,205,50, 0.5)",
      below: "rgba(220,20,60, 0.5)",
    },
    data: finalPerformanceTimeline,
    //borderColor: 'rgba(223, 239, 254, 0.5)',
  });

  const walletTotals = {
    totalInvested: totalInvested,
    totalWalletValue: Math.floor(totalWalletValue),
    result: Math.floor(totalWalletValue - totalInvested),
    performancePct: ((totalWalletValue - totalInvested) / totalInvested) * 100,
  };

  const walletStats: WalletStats = {
    totals: walletTotals,
    globalFirstTs: globalFirstTs,
    globalLastTs: globalLastTs,
    chart: chart,
  };

  return walletStats;
}

const walletTitlesSx = { typography: { xs: "body1", md: "h6" } };
const walletValuesSx = { typography: { xs: "h6", md: "h5" } };

interface WalletProps {
  positions: Position[];
  priceData: AssetPriceData[];
}

export default function Wallet({ positions, priceData }: WalletProps) {
  const { localization } = useLocalization();

  const computeStats = useCallback(() => {
    return computeWalletStats(positions, priceData);
  }, [positions, priceData]);

  const walletStats = useMemo(() => {
    return computeStats();
  }, [computeStats]);

  return (
    <>
      {walletStats === null || walletStats.totals.totalInvested < 1 ? (
        <Box display='flex' justifyContent='center'>
          <Typography startDecorator={<InfoOutlined />} mb={2} mt={2}>
            {localization.body.noPurchases}
          </Typography>
        </Box>
      ) : (
        <Grid
          container
          direction='row'
          justifyContent='space-between'
          alignItems='flex-start'
          mt={2}
          ml={4}
          mr={4}
          mb={4}
        >
          <Grid xs={12} md={2} mb={2} textAlign='center'>
            <Typography sx={walletTitlesSx}>
              {localization.body.totalInvested}
            </Typography>
            <Typography variant='outlined' color='neutral' sx={walletValuesSx}>
              R$ {walletStats.totals.totalInvested}
            </Typography>
          </Grid>
          <Grid xs={12} md={2} mb={2} textAlign='center'>
            <Typography sx={walletTitlesSx}>
              {localization.body.currentValue}
            </Typography>
            <Typography variant='outlined' color='neutral' sx={walletValuesSx}>
              R$ {walletStats.totals.totalWalletValue}
            </Typography>
          </Grid>
          <Grid xs={12} md={2} mb={2} textAlign='center'>
            <Typography sx={walletTitlesSx}>
              {localization.body.result}
            </Typography>
            {walletStats.totals.result >= 0 ? (
              <Typography
                variant='outlined'
                color='success'
                sx={walletValuesSx}
              >
                R$ +{walletStats.totals.result}
              </Typography>
            ) : (
              <Typography variant='outlined' color='danger' sx={walletValuesSx}>
                R$ {walletStats.totals.result}
              </Typography>
            )}
          </Grid>
          <Grid xs={12} md={2} mb={2} textAlign='center'>
            <Typography sx={walletTitlesSx}>
              {localization.body.performance}
            </Typography>
            {walletStats.totals.performancePct >= 0 ? (
              <Typography
                variant='outlined'
                color='success'
                sx={walletValuesSx}
              >
                +{walletStats.totals.performancePct.toFixed(2)}%
              </Typography>
            ) : (
              <Typography variant='outlined' color='danger' sx={walletValuesSx}>
                {walletStats.totals.performancePct.toFixed(2)}%
              </Typography>
            )}
          </Grid>
        </Grid>
      )}
      {walletStats !== null && (
        <>
          <Charts chartData={walletStats.chart} />
        </>
      )}
    </>
  );
}
