import "@fontsource/public-sans";

import { useCallback, useEffect, useState } from "react";

import AssetSelector from "~/components/simulator/AssetSelector";
import { Typography, Grid, Divider } from "@mui/joy";
import Wallet from "~/components/simulator/Wallet";
import type {
  Asset,
  Position,
  AssetPriceData,
  SelectedAsset,
  HandleAssetSelected,
} from "~/interfaces/simulator";
import AssetList from "~/components/simulator/AssetList";
import useLocalization from "~/utils/useLocalization";
import useLocalStorage from "~/utils/useLocalStorage";

const getMinMaxDate = (assetPriceData: AssetPriceData): [string, string] => {
  const minDate = new Date(assetPriceData.data[0]!.date * 1000)
    .toISOString()
    .split("T")[0];
  const maxDate = new Date(
    assetPriceData.data[assetPriceData.data.length - 1]!.date * 1000
  )
    .toISOString()
    .split("T")[0];

  return [minDate ?? "", maxDate ?? ""];
};

const dividerTextSx = { typography: { xs: "body1", md: "h6" } };

export default function Simulator() {
  const { localization } = useLocalization();

  const [assets, setAssets] = useLocalStorage<SelectedAsset[]>("assets", []);

  const [positions, setPositions] = useLocalStorage<Position[]>(
    "positions",
    []
  );

  const [priceData, setPriceData] = useState<AssetPriceData[]>([]);

  const [assetLoading, setAssetLoading] = useState(false);

  const [mounted, setMounted] = useState(false);

  const addAsset = useCallback(
    (asset: Asset, thisAssetPriceData: AssetPriceData) => {
      const [minDate, maxDate] = getMinMaxDate(thisAssetPriceData);
      const newAsset: SelectedAsset = { ...asset, minDate, maxDate };
      setAssets([...assets, newAsset]);
    },
    [assets]
  );

  const updateAsset = useCallback(
    (
      asset: SelectedAsset,
      assetIdx: number,
      thisAssetPriceData: AssetPriceData
    ) => {
      const [minDate, maxDate] = getMinMaxDate(thisAssetPriceData);
      const updatedAsset: SelectedAsset = { ...asset, minDate, maxDate };
      setAssets([
        ...assets.slice(0, assetIdx),
        updatedAsset,
        ...assets.slice(assetIdx + 1),
      ]);
    },
    [assets]
  );

  const addAssetPriceData = useCallback(
    (assetPriceData: AssetPriceData) => {
      setPriceData([...priceData, assetPriceData]);
    },
    [priceData]
  );

  const updateAssetPriceData = useCallback(
    (assetPriceData: AssetPriceData, assetPriceDataIdx: number) => {
      const updatedPriceData = {
        ...priceData[assetPriceDataIdx],
        ...assetPriceData,
      };
      setPriceData([
        ...priceData.slice(0, assetPriceDataIdx),
        updatedPriceData,
        ...priceData.slice(assetPriceDataIdx + 1),
      ]);
    },
    [priceData]
  );

  // no price data example: BTRA11
  const handleAssetSelected = useCallback<HandleAssetSelected>(
    (asset) => {
      if (asset === null) return;

      const selectedAssetIdx = assets.findIndex(
        (thisAsset) => thisAsset.id === asset.id
      );
      const assetPriceDataIdx = priceData.findIndex(
        (thisPriceData) => thisPriceData.ticker === asset.ticker
      );
      const assetIsSelected = selectedAssetIdx > -1 ? true : false;
      const assetHasPriceData = assetPriceDataIdx > -1 ? true : false;

      // 1. asset already selected, asset already has price data, return
      if (assetIsSelected && assetHasPriceData) {
        return;
      } else {
        setAssetLoading(true);
      }

      // 2. asset is not selected and doesnt have price data
      // add asset, add price data
      if (!assetIsSelected && !assetHasPriceData) {
        void fetch(`/api/prices/${asset.ticker}`)
          .then((res) => res.json())
          .then((assetPriceData: AssetPriceData) => {
            if (assetPriceData.data !== null) {
              addAssetPriceData(assetPriceData);
              addAsset(asset, assetPriceData);
            } else {
              alert(
                `Não encontrei dados de preços para o ativo ${asset.ticker}!`
              );
            }
            setAssetLoading(false);
          });
      }
      // 3. asset is not selected and has price data
      // add asset, update price data
      else if (!assetIsSelected && assetHasPriceData) {
        void fetch(`/api/prices/${asset.ticker}`)
          .then((res) => res.json())
          .then((assetPriceData: AssetPriceData) => {
            if (assetPriceData.data !== null) {
              updateAssetPriceData(assetPriceData, assetPriceDataIdx);
              addAsset(asset, assetPriceData);
            }
            setAssetLoading(false);
          });
      }
      // 4. asset is selected but doesnt have price data
      // update asset, add price data
      else if (assetIsSelected && !assetHasPriceData) {
        void fetch(`/api/prices/${asset.ticker}`)
          .then((res) => res.json())
          .then((assetPriceData: AssetPriceData) => {
            if (assetPriceData.data !== null) {
              addAssetPriceData(assetPriceData);
              updateAsset(
                asset as SelectedAsset,
                selectedAssetIdx,
                assetPriceData
              );
            }
            setAssetLoading(false);
          });
      }

      // all branches checked
    },
    [assets, priceData]
  );

  useEffect(() => {
    setMounted(true);
    let ignore = false;
    const fetchAllPriceData = async () => {
      const priceDataPromises = assets.map((asset) =>
        fetch(`/api/prices/${asset.ticker}`).then((res) => res.json())
      );
      const allPriceData = await Promise.all(priceDataPromises).then(
        (values) => values.filter((v) => v !== null) as AssetPriceData[]
      );
      if (!ignore) {
        setPriceData(allPriceData);
      }
    };
    void fetchAllPriceData();
    return () => {
      ignore = true;
    };
  }, []);

  if (!mounted) return <></>;
  return (
    <Grid
      sx={{ flexGrow: 1 }}
      container
      spacing={0}
      justifyContent='center'
      alignItems='center'
      mt={8}
      paddingBottom={8}
    >
      <Grid xs={12} md={10} lg={8} justifyContent='center' alignItems='center'>
        <Divider role='presentation'>
          <Typography mb={2} mt={2} sx={dividerTextSx}>
            {localization.body.selectAssets}
          </Typography>
        </Divider>
        <AssetSelector handleAssetSelected={handleAssetSelected} />
        <Divider role='presentation'>
          <Typography mb={2} mt={2} sx={dividerTextSx}>
            {localization.body.selectedAssets}
          </Typography>
        </Divider>
        <AssetList
          assets={assets}
          setAssets={setAssets}
          positions={positions}
          setPositions={setPositions}
          assetLoading={assetLoading}
        />
        <Divider role='presentation'>
          <Typography mb={2} mt={2} sx={dividerTextSx}>
            {localization.body.portfolio}
          </Typography>
        </Divider>
        <Wallet positions={positions} priceData={priceData} />
      </Grid>
    </Grid>
  );
}
