"use client";
import { useState, useEffect } from "react";

import InventoryItemView from "./Components/InventoryItemView";
import StakingSourcesView from "./Components/StakingSourcesView";
import BankAccountsView from "./Components/BankAccountsView";
import StoreItemView from "./Components/StoreItemView";
import ResourceStore from "./Components/ResourceStore";
import NotificationPopover from "./Components/NotificationPopover";
import { gameConfig } from "@/utils/gameLogic";
import {
  IInventoryItem,
  IStakingSource,
  IBankAccount,
  IStake,
  Notification,
} from "typings";

export default function Home() {
  const [inventoryItems, setInventoryItems] = useState<
    IInventoryItem[] | null | undefined
  >([]);
  const [stakingSources, setStakingSources] = useState<IStakingSource[] | null>(
    []
  );
  const [bankAccount, setBankAccount] = useState<IBankAccount>();
  const [userStakes, setUserStakes] = useState<IStake[]>([]);
  const [lightBoxIsActive, setLightBoxIsActive] = useState(false);
  const [userId, setUserId] = useState<number | null>(null);
  const [notification, setNotification] = useState<Notification | null>(null);
  const [notificationTime, setNotificationTime] = useState(30);
  const [storeItems, setStoreItems] = useState(gameConfig.store);

  const isOwned = (storeItemId: string) => {
    if (inventoryItems) {
      return inventoryItems?.some((item) => item.storeItem.id == storeItemId);
    } else {
      return false;
    }
  };

  const fetchInventoryItems = async () => {
    const response = await fetch(`/api/user/${userId}/inventory-items`);
    const DBInventoryItems = await response.json();

    for (const invItem of DBInventoryItems) {
      invItem.storeItem = gameConfig.store.find(
        (item) => item.id === invItem.storeItemId
      );
    }

    setInventoryItems(DBInventoryItems);
  };

  const fetchStakingSources = async () => {
    const response = await fetch(`/api/user/${userId}/staking-sources`);
    const sources = await response.json();
    setStakingSources(sources);
  };

  const fetchStakes = async () => {
    const response = await fetch(`/api/user/${userId}/stakes`);
    const stakes = await response.json();
    setUserStakes(
      stakes.map((stake: IStake) => ({
        ...stake,
        unclaimed: stake.unclaimed == 1,
      }))
    );
  };

  useEffect(() => {
    const fetchUser = async (wallet: string) => {
      const response = await fetch(`/api/user/login`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          wallet: wallet,
        }),
      });
      if (response.status === 200) {
        console.log("Logged in");
        const { userId } = await response.json();
        setUserId(userId);
      }
    };

    const fetchBankAccount = async () => {
      const response = await fetch(`/api/user/${userId}/bank-account`);
      const bankAccount = await response.json();
      setBankAccount(bankAccount);
    };

    fetchUser("abcdefg"); // Public key goes here
    // Nico is there a better way of checking if a user is logged in?
    if (userId) {
      fetchBankAccount();
      fetchStakingSources();
      fetchInventoryItems();
      fetchStakes();
    }
  }, [userId]);

  // Update data
  useEffect(() => {
    const updatedStoreItems = storeItems.map((storeItem) => {
      return {
        ...storeItem,
        isOwned: isOwned(storeItem.id),
      };
    });
    updatedStoreItems && setStoreItems(updatedStoreItems);
  }, [inventoryItems]);

  // Hide error automatically
  useEffect(() => {
    if (notification) {
      const intervalId = setInterval(() => {
        setNotificationTime((prev) => prev - 1);
      }, 1000);
      if (notificationTime === 0) {
        setNotification(null);
        clearInterval(intervalId);
        setNotificationTime(30);
      }

      return () => {
        clearInterval(intervalId);
      };
    }
  }, [notification, notificationTime]);

  const claimStake = async (stakingEventId: number) => {
    const response = await fetch(`/api/user/${userId}/stakes/claim`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ stakingEventId: stakingEventId }),
    });
    return await response.json();
  };

  const startStake = async (inventoryItemId: number, wellId: number) => {
    const response = await fetch(`/api/user/${userId}/stakes/start`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        inventoryItemId: inventoryItemId,
        wellId: wellId,
      }),
    });
    if (!response.ok) {
      const error = await response.text();
      setNotification({ message: error, type: "Error" });
    }
    if (response.status == 200) {
      const data = await response.json();
      setNotification({
        message: `You've successfully staked`,
        type: "Success",
      });
      fetchStakes();
      fetchStakingSources();
    }
  };

  // Which object is it?
  const sellResource = async (obj: any) => {
    const response = await fetch(`/api/user/${userId}/bank-account`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        obj: obj,
      }),
    });
    return await response.json();
  };

  const buyStoreItem = async (itemId: string) => {
    try {
      const response = await fetch(`/api/user/${userId}/inventory-items`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          itemId: itemId,
        }),
      });

      if (!response.ok) {
        const error = await response.text();
        setNotification({ message: error, type: "Error" });
        return;
      }

      const data = await response.json();

      if (response.status == 200) {
        // Return success message
        console.log(data.message);
        setNotification({
          message: "Item has been purchased",
          type: "Success",
        });
        fetchInventoryItems();
      }
    } catch (error) {
      if (error instanceof Error) {
        setNotification({ message: error.message, type: "Error" });
      } else {
        setNotification({ message: "An unknown error occured", type: "Error" });
      }
    }
  };

  const upgradeInventoryItem = async (itemId: number) => {
    try {
      const response = await fetch(`/api/user/${userId}/inventory-items`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          itemId: itemId,
        }),
      });

      if (!response.ok) {
        const error = await response.text();
        setNotification({ message: error, type: "Error" });
        return;
      }

      const data = await response.json();

      if (response.status == 200) {
        // Return success message
        console.log(data);
        setNotification({ message: "Item has been upgraded", type: "Success" });
        fetchInventoryItems();
      }

      if (response.status == 400) {
        // return error message
        setNotification({ message: data.error, type: "Error" });
      }
    } catch (error) {
      if (error instanceof Error) {
        setNotification({ message: error.message, type: "Error" });
      } else {
        setNotification({ message: "An unknown error occured", type: "Error" });
      }
    }
  };

  const createStakingSource = async () => {
    const response = await fetch(`/api/user/${userId}/staking-sources`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({}),
    });
    return await response.json();
  };

  const handleSetLightBox = () => {
    setLightBoxIsActive(!lightBoxIsActive);
  };

  const handleCloseNotification = () => {
    setNotification(null);
  };

  if (!userId) return <p>Please login</p>;

  return (
    <>
      {notification && (
        <NotificationPopover
          notification={notification}
          onClose={handleCloseNotification}
        />
      )}
      <BankAccountsView
        bankAccount={bankAccount}
        setLightBoxIsActive={handleSetLightBox}
      />
      {lightBoxIsActive && (
        <ResourceStore
          bankAccount={bankAccount}
          setLightBoxIsActive={handleSetLightBox}
        />
      )}
      <div className="grid grid-cols-3 gap-8 p-4">
        <StakingSourcesView
          stakingSources={stakingSources}
          inventoryItems={inventoryItems}
          claimStake={claimStake}
          startStake={startStake}
        />
        <InventoryItemView
          stakingSources={stakingSources}
          inventoryItems={inventoryItems}
          upgradeInventoryItem={upgradeInventoryItem}
        />
        <StoreItemView
          inventoryItems={inventoryItems}
          storeItems={storeItems}
          buyStoreItem={buyStoreItem}
        />
      </div>
    </>
  );
}

// Pass data from storeComponent to parentComponent
// re-fetch data of inventory
