import dynamic from "next/dynamic";
import React, { useEffect, useRef, useState } from "react";
import {
  Button,
  ButtonGroup,
  Dropdown,
  DropdownButton,
  OverlayTrigger,
  Tooltip,
} from "react-bootstrap";
import { BiMicrophone, BiMicrophoneOff } from "react-icons/bi";
import { RiMic2Line } from "react-icons/ri";
import { MdCameraswitch, MdHeadset } from "react-icons/md";
import { AiFillEye, AiFillSetting } from "react-icons/ai";
import { BiUserPin } from "react-icons/bi";
import { HiViewGridAdd } from "react-icons/hi";
import styles from "../../styles/Jitsi.module.css";
import { FaRocketchat } from "react-icons/fa";
import { FiUsers } from "react-icons/fi";

const JitsiMeeting = dynamic(
  () => import("@jitsi/react-sdk").then((mod) => mod.JitsiMeeting),
  { ssr: false }
);

const rtmp = process.env.NEXT_PUBLIC_ROCKET_CHAT_GREENROOM_RTMP;

const Jitsibroadcaster = ({ room, disName, rtmpSrc, handleChat }) => {
  const apiRef = useRef();
  const [logItems, updateLog] = useState([]);
  const [knockingParticipants, updateKnockingParticipants] = useState([]);
  const [mute, setMute] = useState(true);
  const [name, setName] = useState(null);
  const dataArr = [
    { speaker: "A", hour: "10" },
    { speaker: "B", hour: "20" },
    { speaker: "C", hour: "30" },
    { speaker: "D", hour: "40" },
    { speaker: "Z", hour: "50" },
  ];

  const handleDisplayName = async (hr) => {
    const tar = dataArr.find((o) => o.hour === hr);
    if (!tar || tar.speaker == name) {
      return;
    }
    setName(tar.speaker);
    await apiRef.current.executeCommand("displayName", tar.speaker);
  };

  useEffect(() => {
    setInterval(() => {
      const tada = new Date();
      handleDisplayName(tada.getHours().toString());
    }, 900000);
  }, []);

  const printEventOutput = (payload) => {
    updateLog((items) => [...items, JSON.stringify(payload)]);
  };

  const handleAudioStatusChange = (payload, feature) => {
    if (payload.muted) {
      updateLog((items) => [...items, `${feature} off`]);
    } else {
      updateLog((items) => [...items, `${feature} on`]);
    }
  };

  const handleChatUpdates = (payload, ref) => {
    if (payload.isOpen || !payload.unreadCount) {
      return;
    }
    ref.current.executeCommand("toggleChat");
    updateLog((items) => [
      ...items,
      `you have ${payload.unreadCount} unread messages`,
    ]);
  };

  const handleKnockingParticipant = (payload) => {
    updateLog((items) => [...items, JSON.stringify(payload)]);
    updateKnockingParticipants((participants) => [
      ...participants,
      payload?.participant,
    ]);
  };

  const resolveKnockingParticipants = (ref, condition) => {
    knockingParticipants.forEach((participant) => {
      ref.current.executeCommand(
        "answerKnockingParticipant",
        participant?.id,
        condition(participant)
      );
      updateKnockingParticipants((participants) =>
        participants.filter((item) => item.id === participant.id)
      );
    });
  };

  const handleJitsiIFrameRef1 = (iframeRef) => {
    iframeRef.style.border = "10px solid cadetblue";
    iframeRef.style.background = "cadetblue";
    iframeRef.style.height = "25em";
    iframeRef.style.width = "75%";
  };

  const showDevices = async (ref) => {
    const videoInputs = [];
    // get all available video input
    const devices = await ref.current.getAvailableDevices();

    for (const [key, value] of Object.entries(devices)) {
      if (key == "videoInput") {
        value.forEach((vid) => {
          videoInputs.push(vid.label);
        });
      }
    }
    // log for debug
    updateLog((items) => [...items, JSON.stringify(videoInputs)]);

    let nextDevice = "";
    let devs = await ref.current.getCurrentDevices();

    for (const [key, value] of Object.entries(devs)) {
      if (key == "videoInput") {
        updateLog((items) => [...items, "found " + JSON.stringify(value)]);
        let devLabel = value.label;
        let idx = 0;
        videoInputs.forEach((vid) => {
          if (devLabel == vid) {
            let cur = idx + 1;
            if (cur >= videoInputs.length) {
              nextDevice = videoInputs[0];
            } else {
              nextDevice = videoInputs[cur];
              updateLog((items) => [...items, "next is " + nextDevice]);
            }
          }
          idx++;
        });
      }
    }
    updateLog((items) => [...items, "switching to " + nextDevice]);

    await ref.current.setVideoInputDevice(nextDevice);
  };

  const showAudioOutDevices = async (ref) => {
    const audioOutputs = [];
    // get all available audio output
    const devices = await ref.current.getAvailableDevices();

    for (const [key, value] of Object.entries(devices)) {
      if (key == "audioOutput") {
        value.forEach((vid) => {
          audioOutputs.push(vid.label);
        });
      }
    }
    // log for debug
    updateLog((items) => [...items, JSON.stringify(audioOutputs)]);

    let nextDevice = "";
    let devs = await ref.current.getCurrentDevices();

    for (const [key, value] of Object.entries(devs)) {
      if (key == "audioOutput") {
        updateLog((items) => [...items, "found " + JSON.stringify(value)]);
        let devLabel = value.label;
        let idx = 0;
        audioOutputs.forEach((vid) => {
          if (devLabel == vid) {
            let cur = idx + 1;
            if (cur >= audioOutputs.length) {
              nextDevice = audioOutputs[0];
            } else {
              nextDevice = audioOutputs[cur];
              updateLog((items) => [...items, "next is " + nextDevice]);
            }
          }
          idx++;
        });
      }
    }
    updateLog((items) => [...items, "switching to " + nextDevice]);

    await ref.current.setAudioOutputDevice(nextDevice);
  };

  const showAudioDevice = async (ref) => {
    const audioInputs = [];
    // get all available audio input
    const devices = await ref.current.getAvailableDevices();

    for (const [key, value] of Object.entries(devices)) {
      if (key == "audioInput") {
        value.forEach((vid) => {
          audioInputs.push(vid.label);
        });
      }
    }
    // log for debug
    updateLog((items) => [...items, JSON.stringify(audioInputs)]);

    let nextDevice = "";
    let devs = await ref.current.getCurrentDevices();

    for (const [key, value] of Object.entries(devs)) {
      if (key == "audioInput") {
        updateLog((items) => [...items, "found " + JSON.stringify(value)]);
        let devLabel = value.label;
        let idx = 0;
        audioInputs.forEach((vid) => {
          if (devLabel == vid) {
            let cur = idx + 1;
            if (cur >= audioInputs.length) {
              nextDevice = audioInputs[0];
            } else {
              nextDevice = audioInputs[cur];
              updateLog((items) => [...items, "next is " + nextDevice]);
            }
          }
          idx++;
        });
      }
    }
    updateLog((items) => [...items, "switching to " + nextDevice]);
    await ref.current.setAudioInputDevice(nextDevice);
  };

  const handleApiReady = async (apiObj, ref) => {
    ref.current = apiObj;
    await ref.current.addEventListeners({
      // Listening to events from the external API
      audioMuteStatusChanged: (payload) =>
        handleAudioStatusChange(payload, "audio"),
      videoMuteStatusChanged: (payload) =>
        handleAudioStatusChange(payload, "video"),
      raiseHandUpdated: printEventOutput,
      tileViewChanged: printEventOutput,
      chatUpdated: (payload) => handleChatUpdates(payload, ref),
      knockingParticipant: handleKnockingParticipant,
    });

    await ref.current.executeCommand("toggleFilmStrip");
  };

  // Multiple instances demo
  const showUsers = async (ref, which) => {
    try {
      const pinfo = await ref.current.getParticipantsInfo();
      updateLog((items) => [
        ...items,
        "participantes " + JSON.stringify(pinfo),
      ]);
      await ref.current.executeCommand("setTileView", false);
      await ref.current.setLargeVideoParticipant(pinfo[which].participantId);
    } catch (e) {
      console.error("Participant not found!");
      return;
    }
  };

  const makeTile = (ref) => {
    ref.current.executeCommand("setTileView", true);
  };

  const renderStream = (key) => (
    <div className={styles.streamButton}>
      <ButtonGroup className="m-auto">
        <Button
          variant="warning"
          title="Click to start streaming"
          onClick={() =>
            apiRef.current.executeCommand("startRecording", {
              mode: "stream",
              rtmpStreamKey: key,
              youtubeStreamKey: "",
            })
          }
        >
          Go live!
        </Button>
      </ButtonGroup>
    </div>
  );

  const toggleDevice = () => (
    <div className={styles.device}>
      <Button disabled variant="light">
        <AiFillSetting size={20} />
      </Button>
      <ButtonGroup vertical className="m-auto">
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">Microphone Device</Tooltip>}
        >
          <Button
            title="Click to switch audio devices"
            onClick={() => showAudioDevice(apiRef)}
          >
            <RiMic2Line size={20} />
          </Button>
        </OverlayTrigger>
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">Camera Device</Tooltip>}
        >
          <Button
            title="Click to switch video devices"
            onClick={() => showDevices(apiRef)}
          >
            <MdCameraswitch size={20} />
          </Button>
        </OverlayTrigger>
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">Audio Device</Tooltip>}
        >
          <Button
            title="Click to switch audio devices"
            onClick={() => showAudioOutDevices(apiRef)}
          >
            <MdHeadset size={20} />
          </Button>
        </OverlayTrigger>
      </ButtonGroup>
    </div>
  );

  const toggleView = () => (
    <div className={styles.view}>
      <Button variant="light" disabled>
        <AiFillEye size={20} />
      </Button>
      <ButtonGroup vertical className="m-auto">
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">Tile View</Tooltip>}
        >
          <Button
            variant="secondary"
            onClick={() => makeTile(apiRef)}
            title="Click to toggle tile view"
          >
            <HiViewGridAdd size={20} />
          </Button>
        </OverlayTrigger>
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">First User</Tooltip>}
        >
          <Button onClick={() => showUsers(apiRef, 0)} variant="secondary">
            <BiUserPin size={20} />
          </Button>
        </OverlayTrigger>
        <OverlayTrigger
          overlay={<Tooltip id="tooltip-disabled">Second User</Tooltip>}
        >
          <Button onClick={() => showUsers(apiRef, 1)} variant="secondary">
            <FiUsers size={20} />
          </Button>
        </OverlayTrigger>
      </ButtonGroup>
    </div>
  );

  const toolButton = () => (
    <div className={styles.deviceButton}>
      <ButtonGroup className="m-auto">
        <Button
          variant="success"
          title="Click to toogle audio"
          onClick={() => {
            apiRef.current.executeCommand("toggleAudio");
            setMute(!mute);
          }}
        >
          {mute ? <BiMicrophoneOff /> : <BiMicrophone />}
        </Button>
        <DropdownButton variant="danger" as={ButtonGroup} title="End">
          <Dropdown.Item
            as="button"
            onClick={() => apiRef.current.executeCommand("hangup")}
          >
            Leave Meet
          </Dropdown.Item>
          <Dropdown.Item
            variant="danger"
            as="button"
            onClick={() => apiRef.current.stopRecording("stream")}
          >
            End for everyone!
          </Dropdown.Item>
        </DropdownButton>
        <Button color="#f5455c" onClick={handleChat}>
          <FaRocketchat />
        </Button>
      </ButtonGroup>
    </div>
  );

  const renderLog = () =>
    logItems.map((item, index) => (
      <div
        style={{
          fontFamily: "monospace",
          padding: "5px",
        }}
        key={index}
      >
        {item}
      </div>
    ));

  const renderSpinner = () => (
    <div
      style={{
        fontFamily: "sans-serif",
        textAlign: "center",
      }}
    >
      Loading..
    </div>
  );

  return (
    <>
      {rtmp ? renderStream(rtmp) : rtmpSrc && renderStream(rtmpSrc)}
      <div className={styles.jitsiContainer}>
        {toggleDevice()}

        <JitsiMeeting
          domain="meet.jit.si"
          roomName={room}
          spinner={renderSpinner}
          onApiReady={(externalApi) => handleApiReady(externalApi, apiRef)}
          getIFrameRef={handleJitsiIFrameRef1}
          configOverwrite={{
            startWithAudioMuted: true,
            disableModeratorIndicator: true,
            startScreenSharing: false,
            enableEmailInStats: false,
            toolbarButtons: [],
            enableWelcomePage: false,
            prejoinPageEnabled: false,
            startWithVideoMuted: false,
            liveStreamingEnabled: true,
            disableSelfView: false,
            disableSelfViewSettings: true,
            disableShortcuts: true,
            disable1On1Mode: true,
            p2p: {
              enabled: false,
            },
          }}
          interfaceConfigOverwrite={{
            DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
            FILM_STRIP_MAX_HEIGHT: 0,
            TILE_VIEW_MAX_COLUMNS: 0,
            VIDEO_QUALITY_LABEL_DISABLED: true,
          }}
          userInfo={{
            displayName: disName,
          }}
        />
        {toggleView()}
      </div>
      {toolButton()}
      <div className={styles.log}>{renderLog()}</div>
    </>
  );
};

export default Jitsibroadcaster;