/**
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2026 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { formatSince } from '@global-utils/format';
import Dialog from '@mui/material/Dialog';
import { styled } from '@mui/material/styles';
import { useLobbyDataProvider } from '@mine-hooks';
import { bool, func, string } from 'prop-types';

const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc, isEnvDev = false }) => {
  const [players, setPlayers] = useState([]);
  const [search, setSearch] = useState('');
  const [loading, setLoading] = useState(false);
  const [refreshKey, setRefreshKey] = useState(0);
  const [snapshotLoaded, setSnapshotLoaded] = useState(false);
  const [challengingGameAssoc, setChallengingGameAssoc] = useState(null);
  const [declinedMsg, setDeclinedMsg] = useState('');
  const [waitingCountdown, setWaitingCountdown] = useState(0);
  const declinedTimerRef = useRef(null);
  const { waitingPlayersQuery, challengeMutation } = useLobbyDataProvider();

  const addPlayer = useCallback(entry => {
    setPlayers(prev =>
      prev.some(p => p.gameAssoc === entry.gameAssoc)
        ? prev
        : [...prev, entry],
    );
  }, []);

  const removePlayer = useCallback(gameAssoc => {
    setPlayers(prev => prev.filter(p => p.gameAssoc !== gameAssoc));
  }, []);

  useEffect(() => {
    if (!open) return;
    setLoading(true);
    setSnapshotLoaded(false);

    waitingPlayersQuery.refetch().then(result => {
      if (result.data) {
        // Filter out current user's game from the snapshot
        const filtered = result.data.filter(p => p.gameAssoc !== currentGameAssoc);
        setPlayers(filtered);
      }
      setSnapshotLoaded(true);
      setLoading(false);
    }).catch(() => {
      setPlayers([]);
      setSnapshotLoaded(true);
      setLoading(false);
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, refreshKey, currentGameAssoc]);

  useEffect(() => {
    if (!open || !snapshotLoaded) return;
    setSearch('');

    const wrapper = document.getElementById('mine-wrapper');
    const hubUrl = wrapper?.dataset?.mercureHubUrl;
    const jwt = wrapper?.dataset?.mercureSubscriberJwt;
    if (!hubUrl) return;

    const url = new URL(hubUrl, window.location.origin);
    url.searchParams.append('topic', 'mineseeker/lobby');
    if (jwt) url.searchParams.append('authorization', jwt);

    const es = new EventSource(url.toString());
    es.onmessage = e => {
      const { action, gameAssoc, name, since } = JSON.parse(e.data);
      // Don't add the current user's game to the list
      if (gameAssoc === currentGameAssoc) return;
      if ('join' === action) addPlayer({ gameAssoc, name, since });
      if ('leave' === action) removePlayer(gameAssoc);
    };

    return () => es.close();
  }, [open, snapshotLoaded, addPlayer, removePlayer, currentGameAssoc]);

  useEffect(() => {
    if (challengeMutation.isError) {
      setChallengingGameAssoc(null);
      setWaitingCountdown(0);
    }
  }, [challengeMutation.isError]);

  useEffect(() => {
    const handler = () => {
      setChallengingGameAssoc(null);
      clearTimeout(declinedTimerRef.current);
      setDeclinedMsg('Challenge was not accepted.');
      setWaitingCountdown(0);
      declinedTimerRef.current = setTimeout(() => setDeclinedMsg(''), 3500);
    };
    window.addEventListener('challenge-declined', handler);
    return () => {
      window.removeEventListener('challenge-declined', handler);
      clearTimeout(declinedTimerRef.current);
    };
  }, []);

  useEffect(() => {
    if (!waitingCountdown) return;
    const interval = setInterval(() => {
      setWaitingCountdown(prev => {
        if (1 >= prev) return 0;
        return prev - 1;
      });
    }, 1000);
    return () => clearInterval(interval);
  }, [waitingCountdown]);

  const handleChallenge = player => {
    if (challengingGameAssoc) return;
    setChallengingGameAssoc(player.gameAssoc);
    setDeclinedMsg('');
    setWaitingCountdown(30);

    challengeMutation.mutate(
      { targetGameAssoc: player.gameAssoc, challengerGameAssoc: currentGameAssoc }
    );
  };

  const visible = players
    .filter(p => p.gameAssoc !== currentGameAssoc)
    .filter(p => p.name.toLowerCase().includes(search.toLowerCase()));

  const shown = visible.slice(0, 5);
  const hasMore = 5 < visible.length;

  // Debug: log if currentGameAssoc is undefined or if current user appears
  if (isEnvDev && 0 < players.length) {
    const userInList = players.find(p => p.gameAssoc === currentGameAssoc);

    if (userInList) {
      console.warn('[OnlinePlayersDialog] Current user appears in players list:', { currentGameAssoc, userInList });
    }
  }

  return (
    <StyledDialog
      open={open}
      onClose={0 < waitingCountdown ? undefined : onClose}
    >
      <div className="opd">
        <div className="opd-header">
          <div className="opd-header-text">
            <span className="opd-label">Multiplayer</span>
            <h2 className="opd-title">
              <i className="fa fa-users" />
              Online Players
            </h2>
          </div>
          <div className="opd-header-actions">
            <button
              className={`opd-refresh${loading ? ' opd-refresh--spin' : ''}`}
              onClick={() => { if (0 === waitingCountdown) setRefreshKey(k => k + 1); }}
              disabled={loading || 0 < waitingCountdown}
              aria-label="Refresh"
              title="Refresh list"
            >
              <i className="fa fa-refresh" />
            </button>
            <button
              className="opd-close"
              onClick={() => { if (0 === waitingCountdown) onClose(); }}
              disabled={0 < waitingCountdown}
              aria-label="Close"
            >
              <i className="fa fa-times" />
            </button>
          </div>
        </div>
        {0 < waitingCountdown ? (
          <div className="opd-waiting">
            <i className="fa fa-hourglass-start" />
            <p>Waiting {waitingCountdown} second{1 === waitingCountdown ? '' : 's'} for opponent's answer...</p>
          </div>
        ) : (
          <div className="opd-search-wrap">
            <i className="fa fa-search opd-search-icon" />
            <input
              className="opd-search"
              placeholder="Search by username…"
              value={search}
              onChange={e => setSearch(e.target.value)}
            />
            {search && (
              <button className="opd-search-clear" onClick={() => setSearch('')}>
                <i className="fa fa-times" />
              </button>
            )}
          </div>
        )}
        <div className="opd-list">
          {loading && (
            <div className="opd-empty">
              <i className="fa fa-spinner fa-spin" />
              <p>Loading players…</p>
            </div>
          )}

          {!loading && 0 === shown.length && (
            <div className="opd-empty">
              <i className="fa fa-hourglass-half" />
              <p>
                {search
                  ? 'No players match your search.'
                  : 'No other players are waiting right now.'}
              </p>
            </div>
          )}

          {declinedMsg && (
            <div className="opd-declined">
              <i className="fa fa-times-circle" />
              {' '}{declinedMsg}
            </div>
          )}

          {!loading && shown.map(player => {
            const isWaiting = challengingGameAssoc === player.gameAssoc;
            return (
              <div key={player.gameAssoc} className="opd-row">
                <div className="opd-avatar">
                  {player.name.slice(0, 2).toUpperCase()}
                </div>
                <div className="opd-info">
                  <span className="opd-name">{player.name}</span>
                  <span className="opd-since">
                    <i className="fa fa-clock" />
                    {' '}Waiting {formatSince(player.since)}
                  </span>
                </div>
                <button
                  className={`opd-join${isWaiting ? ' opd-join--waiting' : ''}`}
                  onClick={() => handleChallenge(player)}
                  disabled={!!challengingGameAssoc}
                >
                  <i className={`fa ${isWaiting ? 'fa-spinner fa-spin' : 'fa-play'}`} />
                  {isWaiting ? 'Waiting...' : 'Join'}
                </button>
              </div>
            );
          })}
        </div>

        {!loading && hasMore && (
          <p className="opd-note">
            Showing 5 of {visible.length} waiting players
          </p>
        )}
      </div>
    </StyledDialog>
  );
};

const StyledDialog = styled(Dialog)({
  '& .MuiDialog-paper': {
    background: '#07090d',
    backgroundImage: `
      linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px),
      linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px)
    `,
    backgroundSize: '46px 46px',
    border: '1px solid rgba(35, 111, 135, 0.4)',
    borderRadius: '12px',
    boxShadow: '0 0 80px rgba(35, 111, 135, 0.15), 0 32px 80px rgba(0, 0, 0, 0.9)',
    width: '500px',
    maxWidth: '94vw',
    overflow: 'hidden',
    color: '#fff',
  },
  '& .MuiBackdrop-root': {
    background: 'rgba(2, 4, 8, 0.88)',
    backdropFilter: 'blur(4px)',
  },
});

export default OnlinePlayersDialog;

OnlinePlayersDialog.propTypes = {
  open:              bool.isRequired,
  onClose:           func.isRequired,
  currentGameAssoc:  string,
  isEnvDev:          bool,
};
