// /src/app/[locale]/components/InvitePlayersModal.tsx

'use client'

import { useState, useEffect, useRef, useCallback, useMemo, useTransition } from 'react'
import Modal from './Modal'
import MiniCard from './MiniCard'
import { useSession } from 'next-auth/react'
import LoadingSpinner from './LoadingSpinner'
import { Player, Team } from '../../../types/team'
import Pagination from './Pagination'
import { AnimatePresence, motion } from 'framer-motion'
import Switch from './Switch'

type InviteStatus = 'sent' | 'failed' | 'added' | 'pending'

type Props = {
  show: boolean
  onClose: () => void
  onSuccess: () => void
  team: Team
  directAdd?: boolean
}

type UnknownRec = Record<string, unknown>;

type ApiResultItem = { steamId: string; ok: boolean };

function parseResultsFromJson(json: unknown): ApiResultItem[] | null {
  if (!isRecord(json)) return null;
  if (Array.isArray(json.results)) {
    const out: ApiResultItem[] = [];
    for (const r of json.results) {
      const steamIdVal = isRecord(r) ? r.steamId : undefined;
      const okVal = isRecord(r) ? r.ok : undefined;
      const steamId =
        typeof steamIdVal === 'string' ? steamIdVal
        : typeof steamIdVal === 'number' ? String(steamIdVal)
        : null;
      const ok = typeof okVal === 'boolean' ? okVal : false;
      if (steamId) out.push({ steamId, ok });
    }
    return out;
  }
  if (Array.isArray(json.invitationIds)) {
    const ids: string[] = (json.invitationIds as unknown[]).map(v =>
      typeof v === 'string' ? v : String(v)
    );
    return ids.map(steamId => ({ steamId, ok: true }));
  }
  return null;
}

function isRecord(v: unknown): v is UnknownRec {
  return !!v && typeof v === 'object';
}

function getTeamLeaderSteamId(team: Team | null | undefined): string | null {
  if (!team) return null;
  const leaderUnknown = (team as unknown as { leader?: unknown }).leader;
  if (typeof leaderUnknown === 'string') return leaderUnknown;
  if (isRecord(leaderUnknown) && typeof leaderUnknown.steamId === 'string') {
    return leaderUnknown.steamId;
  }
  return null;
}


export default function InvitePlayersModal({ show, onClose, onSuccess, team, directAdd = false }: Props) {
  const { data: session } = useSession()
  const steamId = session?.user?.steamId

  const [allUsers, setAllUsers] = useState<Player[]>([])
  const [selectedIds, setSelectedIds] = useState<string[]>([])
  const [knownUsers, setKnownUsers] = useState<Record<string, Player>>({})
  const [invitedIds, setInvitedIds] = useState<string[]>([])
  const [isInviting, setIsInviting] = useState(false)
  const [isSuccess, setIsSuccess] = useState(false)
  const [sentCount, setSentCount] = useState(0)
  const [searchTerm, setSearchTerm] = useState('')

  // Toggle: nur Spieler ohne Team
  const [onlyFree, setOnlyFree] = useState(false)

  // Sanftes UI-Update
  const [, startTransition] = useTransition()
  const [isFetching, setIsFetching] = useState(false)
  const abortRef = useRef<AbortController | null>(null)

  // 🔽 Anti-Flacker: verzögert anzeigen + Mindestdauer
  const SPINNER_DELAY_MS = 120
  const SPINNER_MIN_MS = 250
  const [spinnerVisible, setSpinnerVisible] = useState(false)
  const spinnerShowTimer = useRef<number | null>(null)
  const spinnerShownAt = useRef<number | null>(null)

  // Dynamisch berechnete Items pro Seite
  const [usersPerPage, setUsersPerPage] = useState<number>(9)
  const [currentPage, setCurrentPage] = useState(1)
  const [invitedStatus, setInvitedStatus] = useState<Record<string, InviteStatus>>({})

  // Refs / Layout
  const descRef = useRef<HTMLParagraphElement>(null)
  const selectedWrapRef = useRef<HTMLDivElement>(null)
  const searchRef = useRef<HTMLDivElement>(null)
  const successRef = useRef<HTMLDivElement>(null)
  const gridRef = useRef<HTMLDivElement>(null)
  const firstCardRef = useRef<HTMLDivElement>(null)

  const showControls = !isSuccess

  // aktuelle Grid-Höhe halten
  const [gridHoldHeight, setGridHoldHeight] = useState<number>(0)

  useEffect(() => {
    if (!show) return
    void fetchUsers({ resetLayout: true })
    setIsSuccess(false)
    setInvitedIds([])
    setInvitedStatus({})
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [show])

  useEffect(() => {
    if (!show) return
    setCurrentPage(1)
    void fetchUsers({ resetLayout: false })
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [onlyFree])

  useEffect(() => {
    if (!gridRef.current) return
    const observer = new ResizeObserver(() => {
      if (!gridRef.current) return
      const gridHeight = gridRef.current.clientHeight
      const cardHeight = gridRef.current.querySelector('div')?.clientHeight || 0
      const rows = cardHeight > 0 ? Math.floor(gridHeight / cardHeight) : 1
      const cols = window.innerWidth >= 640 ? 3 : 2
      setUsersPerPage(rows * cols)
    })
    observer.observe(gridRef.current)
    return () => observer.disconnect()
  }, [])

  async function fetchUsers(opts: { resetLayout: boolean }) {
    try {
      abortRef.current?.abort();
      const ctrl = new AbortController();
      abortRef.current = ctrl;

      if (gridRef.current) setGridHoldHeight(gridRef.current.clientHeight);

      setIsFetching(true);

      if (spinnerShowTimer.current) window.clearTimeout(spinnerShowTimer.current);
      spinnerShowTimer.current = window.setTimeout(() => {
        setSpinnerVisible(true);
        spinnerShownAt.current = Date.now();
      }, SPINNER_DELAY_MS);

      const qs = new URLSearchParams({ teamId: team.id });
      if (onlyFree) qs.set('onlyFree', 'true');

      const res = await fetch(`/api/team/available-users?${qs.toString()}`, {
        signal: ctrl.signal,
        cache: 'no-store',
      });
      if (!res.ok) throw new Error('load failed');

      const dataUnknown: unknown = await res.json();
      const users = (isRecord(dataUnknown) && Array.isArray(dataUnknown.users))
        ? (dataUnknown.users as Player[])
        : [];

      startTransition(() => {
        setAllUsers(users);
        setKnownUsers(prev => {
          const next = { ...prev };
          for (const u of users) next[u.steamId] = u;
          return next;
        });
        if (opts.resetLayout) {
          setSelectedIds([]);
          setInvitedIds([]);
          setIsSuccess(false);
        }
      });
    } catch (e: unknown) {
      if (isRecord(e) && e.name === 'AbortError') return;
      console.error('Fehler beim Laden der Benutzer:', e);
    } finally {
      setIsFetching(false)
      abortRef.current = null

      // 🔽 Spinner Mindestdauer respektieren
      const hide = () => {
        setSpinnerVisible(false)
        spinnerShownAt.current = null
        setGridHoldHeight(0)
      }

      if (spinnerShowTimer.current) {
        // Wenn der Delay-Timer noch nicht gefeuert hat: einfach abbrechen
        window.clearTimeout(spinnerShowTimer.current)
        spinnerShowTimer.current = null
        // Der Spinner wurde evtl. nie sichtbar -> direkt aufräumen
        if (!spinnerShownAt.current) {
          hide()
          return
        }
      }

      if (spinnerShownAt.current) {
        const elapsed = Date.now() - spinnerShownAt.current
        const remain = Math.max(0, SPINNER_MIN_MS - elapsed)
        if (remain > 0) {
          window.setTimeout(hide, remain)
        } else {
          hide()
        }
      } else {
        // Falls er nie sichtbar war
        hide()
      }
    }
  }

  const handleSelect = (steamId: string) => {
    setSelectedIds(prev => {
      const isSelected = prev.includes(steamId)
      // Beim Hinzufügen: sicherstellen, dass wir die Card-Daten im Cache haben
      if (!isSelected) {
        const u = allUsers.find(u => u.steamId === steamId)
        if (u) {
          setKnownUsers(map => (map[steamId] ? map : { ...map, [steamId]: u }))
        }
      }
      return isSelected ? prev.filter(id => id !== steamId) : [...prev, steamId]
    })
  }

  const handleInvite = async () => {
    if (isInviting) return;
    if (selectedIds.length === 0 || !steamId) return;
    const ids = [...selectedIds];

    try {
      setIsInviting(true);
      const url = directAdd ? '/api/team/add-players' : '/api/team/invite';
      const body = directAdd
        ? { teamId: team.id, steamIds: ids }
        : { teamId: team.id, userIds: ids, invitedBy: steamId };

      const res = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });

      let jsonUnknown: unknown = null;
      try { jsonUnknown = await res.clone().json(); } catch { /* ignore */ }

      let results: ApiResultItem[] | null = parseResultsFromJson(jsonUnknown);

      // Fallback, falls API keine detailierten results liefert
      if (!results) {
        results = ids.map(id => ({ steamId: id, ok: res.ok }));
      }

      const nextStatus: Record<string, InviteStatus> = {};
      let okCount = 0;
      for (const r of results) {
        const st: InviteStatus = r.ok ? (directAdd ? 'added' : 'sent') : 'failed';
        nextStatus[r.steamId] = st;
        if (r.ok) okCount++;
      }

      setInvitedStatus(prev => ({ ...prev, ...nextStatus }));
      setInvitedIds(ids);
      setSentCount(okCount);
      setIsSuccess(true);
      setSelectedIds([]);
      if (okCount > 0) onSuccess();
    } catch (err: unknown) {
      console.error('Fehler beim Einladen:', err);
      setInvitedStatus(prev => ({
        ...prev,
        ...Object.fromEntries(selectedIds.map(id => [id, 'failed' as InviteStatus])),
      }));
      setInvitedIds(selectedIds);
      setSentCount(0);
      setIsSuccess(true);
    } finally {
      setIsInviting(false);
    }
  };

  useEffect(() => { setCurrentPage(1) }, [searchTerm])

  const filteredUsers = useMemo(() => {
    if (isSuccess) return allUsers
    return allUsers.filter(u => u.name?.toLowerCase().includes(searchTerm.toLowerCase()))
  }, [allUsers, searchTerm, isSuccess])

  const unselectedUsers = useMemo(() => {
    if (isSuccess) return filteredUsers
    return filteredUsers.filter(user =>
      !selectedIds.includes(user.steamId) &&
      (!isSuccess || !invitedIds.includes(user.steamId))
    )
  }, [filteredUsers, selectedIds, invitedIds, isSuccess])

  const totalPages = Math.ceil(unselectedUsers.length / Math.max(1, usersPerPage))
  const startIdx = (currentPage - 1) * Math.max(1, usersPerPage)
  const paginatedUsers = unselectedUsers.slice(startIdx, startIdx + Math.max(1, usersPerPage))

  useEffect(() => {
    if (totalPages === 0 && currentPage !== 1) setCurrentPage(1)
    else if (currentPage > totalPages) setCurrentPage(totalPages || 1)
  }, [totalPages, currentPage])

  const recalcUsersPerPage = useCallback(() => {
    const gridEl = gridRef.current
    if (!gridEl) return
    const bodyEl = gridEl.parentElement as HTMLElement | null
    if (!bodyEl) return

    const outer = (el: HTMLElement | null) => {
      if (!el) return 0
      const cs = getComputedStyle(el)
      return el.offsetHeight + parseFloat(cs.marginTop || '0') + parseFloat(cs.marginBottom || '0')
    }

    const gridMT = parseFloat(getComputedStyle(gridEl).marginTop || '0')
    const usedAbove =
      outer(descRef.current) +
      outer(selectedWrapRef.current) +
      outer(searchRef.current) +
      outer(successRef.current) +
      gridMT

    const reserveForPagination = (!isFetching && !isSuccess && totalPages > 1) ? 48 : 0
    const availableHeight = Math.max(0, bodyEl.clientHeight - usedAbove - reserveForPagination)

    const csGrid = getComputedStyle(gridEl)
    const cols = Math.max(1, csGrid.gridTemplateColumns.split(' ').filter(Boolean).length)
    const rowGap = parseFloat(csGrid.rowGap || '0')

    const cardEl = firstCardRef.current
    let cardHeight = cardEl?.offsetHeight || 0
    if (!cardHeight) cardHeight = 140

    const rows = Math.max(1, Math.floor((availableHeight + rowGap) / (cardHeight + rowGap)))
    const nextPerPage = Math.max(1, rows * cols)

    setUsersPerPage(prev => (prev !== nextPerPage ? nextPerPage : prev))
  }, [isFetching, isSuccess, totalPages])

  useEffect(() => {
    recalcUsersPerPage()
    const gridEl = gridRef.current
    if (!gridEl) return
    const bodyEl = gridEl.parentElement as HTMLElement | null
    const cardEl = firstCardRef.current

    const ro = new ResizeObserver(() => recalcUsersPerPage())
    ro.observe(gridEl)
    if (bodyEl) ro.observe(bodyEl)
    if (cardEl) ro.observe(cardEl)

    const onResize = () => recalcUsersPerPage()
    window.addEventListener('resize', onResize)
    const id = window.setTimeout(recalcUsersPerPage, 60)
    return () => {
      window.clearTimeout(id)
      ro.disconnect()
      window.removeEventListener('resize', onResize)
    }
  }, [recalcUsersPerPage, show, filteredUsers.length, isSuccess])

  return (
    <Modal
      id="invite-members-modal"
      title={directAdd ? 'Spieler hinzufügen' : 'Spieler einladen'}
      show={show}
      onClose={onClose}
      onSave={!isSuccess ? (() => { if (!isInviting) handleInvite() }) : undefined}
      closeButtonColor={!isSuccess ? (isSuccess ? 'teal' : 'blue') : undefined}
      closeButtonTitle={
        !isSuccess
          ? (isInviting ? (directAdd ? 'Wird hinzugefügt...' : 'Wird eingeladen...') : (directAdd ? 'Hinzufügen' : 'Einladungen senden'))
          : undefined
      }
      closeButtonLoading={!isSuccess && isInviting}
      scrollBody
    >
      {showControls && (
      <p ref={descRef} className="text-sm text-gray-700 dark:text-neutral-300 mb-2">
        {directAdd
          ? 'Wähle Spieler aus, die du direkt zum Team hinzufügen möchtest:'
          : 'Wähle Spieler aus, die du in dein Team einladen möchtest:'}
      </p>
      )}

      {/* Filterleiste */}
      {showControls && (
        <div ref={searchRef} className="mt-1 grid grid-cols-[auto_1fr] items-center gap-x-3">
          <input
            type="text"
            placeholder="Suchen..."
            value={searchTerm}
            onChange={(e) => setSearchTerm(e.target.value)}
            className="w-40 sm:w-56 rounded border px-2 py-1.5 text-[13px]
                      focus:outline-none focus:ring focus:ring-blue-400
                      dark:bg-neutral-800 dark:border-neutral-600 dark:text-neutral-100"
          />
          <div className="justify-self-end min-w-[240px] sm:min-w-[300px]">
            <Switch
              id="only-free-switch"
              checked={onlyFree}
              onChange={(v) => startTransition(() => setOnlyFree(v))}
              labelLeft="Alle"
              labelRight="Nur ohne Team"
            />
          </div>
        </div>
      )}

      {/* Ausgewählte */}
      {selectedIds.length > 0 && (
        <div ref={selectedWrapRef} className="col-span-full mt-2">
          <h3 className="text-sm font-semibold text-gray-700 dark:text-neutral-300 mb-2">Ausgewählte Spieler:</h3>
          <div className="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-2">
            <AnimatePresence initial={false}>
              {selectedIds.map((id) => {
                const user = knownUsers[id]
                if (!user) return null
                return (
                  <motion.div key={user.steamId} layout initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }}>
                    <MiniCard
                      steamId={user.steamId}
                      title={user.name}
                      avatar={user.avatar}
                      location={user.location}
                      selected
                      onSelect={handleSelect}
                      draggable={false}
                      currentUserSteamId={steamId!}
                      teamLeaderSteamId={getTeamLeaderSteamId(team) ?? undefined}
                      hideActions
                      rank={user.premierRank}
                    />
                  </motion.div>
                )
              })}
            </AnimatePresence>
          </div>
        </div>
      )}

      {isSuccess && (
        <div
          ref={successRef}
          className={`mt-2 px-4 py-2 text-sm rounded-lg border transition-opacity duration-150 ${
            isSuccess ? 'opacity-100' : 'opacity-0'
          }`}
          style={{
            minHeight: 40, // ~ Höhe reservieren, vermeidet Jump
            background: sentCount ? '#dcfce7' : '#fee2e2',
            borderColor: sentCount ? '#bbf7d0' : '#fecaca',
            color: sentCount ? '#166534' : '#991b1b'
          }}
        >
          {isSuccess && (
            <>
              {directAdd
                ? (sentCount === 0
                    ? 'Niemand konnte hinzugefügt werden.'
                    : sentCount === invitedIds.length
                      ? `${sentCount} Mitglied${sentCount === 1 ? '' : 'er'} hinzugefügt!`
                      : `${sentCount} Mitglied${sentCount === 1 ? '' : 'er'} hinzugefügt, andere fehlgeschlagen.`)
                : (sentCount === 0
                    ? 'Keine Einladungen versendet.'
                    : sentCount === invitedIds.length
                      ? `${sentCount} Einladung${sentCount === 1 ? '' : 'en'} versendet!`
                      : `${sentCount} Einladung${sentCount === 1 ? '' : 'en'} versendet, andere fehlgeschlagen.`)}
            </>
          )}
        </div>
      )}

      {/* Grid */}
      <div
        ref={gridRef}
        className="relative grid grid-cols-2 sm:grid-cols-3 gap-4 mt-4 transition-[min-height] duration-150"
        style={gridHoldHeight ? { minHeight: gridHoldHeight } : undefined}
      >
        {unselectedUsers.length === 0 ? (
          <div className="col-span-full text-center text-gray-500 dark:text-neutral-400">
            {allUsers.length === 0
              ? directAdd ? 'Keine Benutzer verfügbar :(' : 'Niemand zum Einladen verfügbar :('
              : 'Keine Benutzer gefunden.'}
          </div>
        ) : (
          <AnimatePresence mode="popLayout" initial={false}>
            {!isSuccess && paginatedUsers.map((user, idx) => (
              <motion.div
                key={user.steamId}
                layout
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                transition={{ duration: 0.2 }}
                ref={idx === 0 ? firstCardRef : undefined}
              >
                <MiniCard
                  steamId={user.steamId}
                  title={user.name}
                  avatar={user.avatar}
                  location={user.location}
                  selected={false}
                  draggable={false}
                  onSelect={handleSelect}
                  currentUserSteamId={steamId!}
                  teamLeaderSteamId={getTeamLeaderSteamId(team) ?? undefined}
                  hideActions
                  rank={user.premierRank}
                  invitedStatus={invitedStatus[user.steamId]}
                />
              </motion.div>
            ))}

            {isSuccess && invitedIds.map((id, idx) => {
              const user = knownUsers[id]
              if (!user) return null
              return (
                <motion.div
                  key={`invited-${user.steamId}`}
                  layout
                  initial={{ opacity: 0, y: 10 }}
                  animate={{ opacity: 1, y: 0 }}
                  exit={{ opacity: 0, y: -10 }}
                  transition={{ duration: 0.2 }}
                  ref={idx === 0 ? firstCardRef : undefined}
                >
                  <MiniCard
                    steamId={user.steamId}
                    title={user.name}
                    avatar={user.avatar}
                    location={user.location}
                    selected={false}
                    draggable={false}
                    currentUserSteamId={steamId!}
                    teamLeaderSteamId={getTeamLeaderSteamId(team) ?? undefined}
                    hideActions
                    rank={user.premierRank}
                    invitedStatus={invitedStatus[user.steamId]}
                  />
                </motion.div>
              )
            })}
          </AnimatePresence>
        )}

        {/* Overlay-Spinner: erscheint verzögert und bleibt min. SPINNER_MIN_MS sichtbar */}
        {spinnerVisible && (
          <div className="absolute inset-0 bg-black/5 dark:bg-white/5 backdrop-blur-[1px] pointer-events-none flex items-center justify-center rounded transition-opacity duration-150">
            <LoadingSpinner />
          </div>
        )}
      </div>

      {/* Pagination */}
      <div className="mt-3 min-h-[44px] flex justify-center items-center">
        {!isFetching && !isSuccess && totalPages > 1 ? (
          <Pagination
            currentPage={currentPage}
            totalPages={totalPages}
            onPageChange={(p) => {
              const next = Math.max(1, Math.min(totalPages, p))
              setCurrentPage(next)
              setTimeout(() => recalcUsersPerPage(), 0)
            }}
          />
        ) : null}
      </div>
    </Modal>
  )
}
