import React, { useState, useContext, useMemo, useEffect, useRef } from "react";
import equal from "fast-deep-equal";
import "./Activities.css";
import { PlayerColor } from "./PlayerColor";
import { Dice } from "./Dice";
import { GlobalContext } from "./GlobalContext";

type ActivityData = {
  message: string;
  color: string;
  date: number;
};

type PlayerData = {
  name: string;
  mustDiscard?: number;
};

type TurnData = {
  color?: string;
  name?: string;
  placedRobber?: boolean;
  robberInAction?: boolean;
  active?: string;
  actions?: string[];
  select?: Record<string, unknown>;
};

type ActivityProps = {
  keep: boolean;
  activity: ActivityData;
};

const Activity: React.FC<ActivityProps> = ({ keep, activity }) => {
  const [animation, setAnimation] = useState("open");
  const [display, setDisplay] = useState(true);

  const hide = async (ms) => {
    await new Promise((r) => setTimeout(r, ms));
    setAnimation("close");
    await new Promise((r) => setTimeout(r, 1000));
    setDisplay(false);
  };

  if (display && !keep) {
    setTimeout(() => {
      hide(10000);
    }, 0);
  }

  let message;
  /* If the date is in the future, set it to now */
  const dice = activity.message.match(/^(.*rolled )([1-6])(, ([1-6]))?(.*)$/);
  if (dice) {
    if (dice[4]) {
      const sum = parseInt(dice[2]) + parseInt(dice[4]);
      message = (
        <>
          {dice[1]}
          <b>{sum}</b>: <Dice pips={dice[2]} />, <Dice pips={dice[4]} />
          {dice[5]}
        </>
      );
    } else {
      message = (
        <>
          {dice[1]}
          <Dice pips={dice[2]} />
          {dice[5]}
        </>
      );
    }
  } else {
    message = activity.message;
  }

  return (
    <>
      {display && (
        <div className={`Activity ${animation}`}>
          <PlayerColor color={activity.color} />
          {message}
        </div>
      )}
    </>
  );
};

const Activities: React.FC = () => {
  const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
  const [activities, setActivities] = useState<ActivityData[]>([]);
  const [turn, setTurn] = useState<TurnData | undefined>(undefined);
  const [color, setColor] = useState<string | undefined>(undefined);
  const [players, setPlayers] = useState<Record<string, PlayerData>>({});
  const [timestamp, setTimestamp] = useState<number>(0);
  const [state, setState] = useState<string>("");

  const fields = useMemo(() => ["activities", "turn", "players", "timestamp", "color", "state"], []);
  const requestUpdate = (fields: string | string[]) => {
    let request: string[];
    if (!Array.isArray(fields)) {
      request = [fields];
    } else {
      request = fields;
    }
    sendJsonMessage({
      type: "get",
      fields: request,
    });
  };
  useEffect(() => {
    if (!lastJsonMessage) {
      return;
    }
    const data = lastJsonMessage;
    switch (data.type) {
      case "game-update": {
        const ignoring: string[] = [],
          processing: string[] = [];
        if (data.update) {
          for (const field in data.update) {
            if (fields.indexOf(field) === -1) {
              ignoring.push(field);
            } else {
              processing.push(field);
            }
          }
        }
        console.log(`activities - game update`, data.update);
        console.log(`activities - ignoring ${ignoring.join(",")}`);
        console.log(`activities - processing ${processing.join(",")}`);

        if (data.update && "state" in data.update && data.update.state !== state) {
          requestUpdate("turn");
          setState(data.update.state as string);
        }
        if (data.update && "activities" in data.update && !equal(data.update.activities, activities)) {
          setActivities(data.update.activities as ActivityData[]);
        }
        if (data.update && "turn" in data.update && !equal(data.update.turn, turn)) {
          setTurn(data.update.turn as TurnData);
        }
        if (data.update && "players" in data.update && !equal(data.update.players, players)) {
          setPlayers(data.update.players as Record<string, PlayerData>);
        }
        if (data.update && "timestamp" in data.update && data.update.timestamp !== timestamp) {
          setTimestamp(data.update.timestamp as number);
        }
        if (data.update && "color" in data.update && data.update.color !== color) {
          setColor(data.update.color as string);
        }
        break;
      }
      default:
        break;
    }
  }, [lastJsonMessage, activities, turn, players, timestamp, color, state, fields]);
  
  useEffect(() => {
    if (!sendJsonMessage) {
      return;
    }
    sendJsonMessage({
      type: "get",
      fields,
    });
  }, [sendJsonMessage, fields]);

  if (!timestamp) {
    return <></>;
  }

  const isTurn = turn && turn.color === color ? true : false,
    normalPlay = ["initial-placement", "normal", "volcano"].indexOf(state) !== -1,
    mustPlaceRobber = turn && !turn.placedRobber && turn.robberInAction,
    placement = state === "initial-placement" || (turn && turn.active === "road-building"),
    placeRoad = placement && turn && turn.actions && turn.actions.indexOf("place-road") !== -1,
    mustStealResource = turn && turn.actions && turn.actions.indexOf("steal-resource") !== -1,
    rollForVolcano = state === "volcano" && turn && !turn.select,
    rollForOrder = state === "game-order",
    selectResources = turn && turn.actions && turn.actions.indexOf("select-resources") !== -1;

  const discarders: React.ReactElement[] = [];
  let mustDiscard = false;
  for (const key in players) {
    const player = players[key];
    if (!player.mustDiscard) {
      continue;
    }
    mustDiscard = true;
    const name = color === key ? "You" : player.name;
    discarders.push(
      <div key={name} className="Requirement">
        {name} must discard <b>{player.mustDiscard}</b> cards.
      </div>
    );
  }

  const list: React.ReactElement[] = activities
    .filter((activity, index) => activities.length - 1 === index || timestamp - activity.date < 11000)
    .map((activity, index, filtered) => {
      return <Activity keep={filtered.length - 1 === index} key={activity.date} activity={activity} />;
    });

  let who: string | React.ReactElement;
  if (turn && turn.select) {
    const selecting: { color: string; name: string }[] = [];
    for (const key in turn.select) {
      selecting.push({
        color: key,
        name: color === key ? "You" : players[key]?.name || "",
      });
    }
    who = (
      <>
        {selecting.map((player, index) => (
          <div className="Who" key={index}>
            <PlayerColor color={player.color} />
            {player.name}
            {index !== selecting.length - 1 ? ", " : ""}
          </div>
        ))}
      </>
    );
  } else {
    if (isTurn) {
      who = "You";
    } else {
      if (!turn || !turn.name) {
        who = "Everyone";
      } else {
        who = (
          <>
            <PlayerColor color={turn.color} />
            {turn.name}
          </>
        );
      }
    }
  }

  return (
    <div className="Activities">
      {list}
      {normalPlay && !mustDiscard && mustPlaceRobber && <div className="Requirement">{who} must move the Robber.</div>}

      {placement && (
        <div className="Requirement">
          {who} must place {
            (turn && (turn as any).active === "road-building" && (turn as any).freeRoads)
              ? `${(turn as any).freeRoads} roads`
              : placeRoad
              ? "a road"
              : "a settlement"
          }.
        </div>
      )}

      {mustStealResource && <div className="Requirement">{who} must select a player to steal from.</div>}

      {rollForOrder && <div className="Requirement">{who} must roll for game order.</div>}

      {rollForVolcano && <div className="Requirement">{who} must roll for Volcano devastation!</div>}

      {selectResources && <div className="Requirement">{who} must select resources!</div>}

      {normalPlay && mustDiscard && <> {discarders} </>}

      {!isTurn && normalPlay && turn && (
        <div>
          It is <PlayerColor color={turn.color} /> {turn.name}
          {"'"}s turn.
        </div>
      )}

      {isTurn && normalPlay && turn && (
        <div className="Go">
          <PlayerColor color={turn.color} /> It is your turn.
        </div>
      )}
    </div>
  );
};

export { Activities };
