import React, { useCallback, useEffect, useRef, useState } from 'react';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import CircularProgress from '@mui/material/CircularProgress';
import LinearProgress from '@mui/material/LinearProgress';
import Tooltip from '@mui/material/Tooltip';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider';
import SendIcon from '@mui/icons-material/Send';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import AttachFileIcon from '@mui/icons-material/AttachFile';
import CloseIcon from '@mui/icons-material/Close';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { nextcloudApi } from '../../services/nextcloud';
import { useChat } from '../../contexts/ChatContext';
import ChatMessage from './ChatMessage';
import type { NextcloudMessage } from '../../types/nextcloud.types';

const LONG_POLL_TIMEOUT = 25;

function dayKey(timestamp: number): string {
  const d = new Date(timestamp * 1000);
  return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
}

function dayLabel(timestamp: number): string {
  const msgDate = new Date(timestamp * 1000);
  const today = new Date();
  const yesterday = new Date();
  yesterday.setDate(today.getDate() - 1);

  const sameDay = (a: Date, b: Date) =>
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate();

  if (sameDay(msgDate, today)) return 'Heute';
  if (sameDay(msgDate, yesterday)) return 'Gestern';
  return msgDate.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
}

const ChatMessageView: React.FC = () => {
  const { selectedRoomToken, selectRoom, rooms, loginName, isActive } = useChat();
  const queryClient = useQueryClient();
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const isInitialLoadRef = useRef(true);
  const [input, setInput] = useState('');
  const [messages, setMessages] = useState<NextcloudMessage[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [uploadProgress, setUploadProgress] = useState<number | null>(null);
  const [isDragOver, setIsDragOver] = useState(false);
  const [replyToMessage, setReplyToMessage] = useState<NextcloudMessage | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const lastMsgIdRef = useRef<number>(0);

  // reactions override map: messageId → { reactions, reactionsSelf }
  const [reactionsMap, setReactionsMap] = useState<Map<number, { reactions: Record<string, number>; reactionsSelf: string[] }>>(new Map());

  const fetchReactions = useCallback(async (msgId: number) => {
    if (!selectedRoomToken) return;
    try {
      const detailed = await nextcloudApi.getReactions(selectedRoomToken, msgId);
      const counts: Record<string, number> = {};
      const self: string[] = [];
      for (const [emoji, actors] of Object.entries(detailed)) {
        counts[emoji] = actors.length;
        if (actors.some((a) => a.actorId === loginName)) self.push(emoji);
      }
      setReactionsMap((prev) => new Map(prev).set(msgId, { reactions: counts, reactionsSelf: self }));
    } catch {
      // ignore — reactions are best-effort
    }
  }, [selectedRoomToken, loginName]);

  // Initial fetch + long-poll loop for near-instant message delivery
  useEffect(() => {
    if (!selectedRoomToken || !isActive) return;

    let cancelled = false;
    let currentAbort: AbortController | null = null;

    async function run() {
      setIsLoading(true);
      setMessages([]);
      setReactionsMap(new Map());
      isInitialLoadRef.current = true;
      lastMsgIdRef.current = 0;

      // Step 1: Initial fetch — Nextcloud returns newest-first, so reverse for chronological display
      currentAbort = new AbortController();
      try {
        const initial = await nextcloudApi.getMessages(selectedRoomToken!, undefined, currentAbort.signal);
        if (cancelled) return;
        // Filter reaction system messages — these appear as chat events but should not be shown
        const displayable = initial.filter(
          (m) => m.systemMessage !== 'reaction' && m.systemMessage !== 'reaction_deleted',
        );
        setMessages([...displayable].reverse());
        lastMsgIdRef.current = initial.length > 0 ? Math.max(...initial.map(m => m.id)) : 0;
        setIsLoading(false);
      } catch {
        if (cancelled) return;
        setIsLoading(false);
      }

      // Step 2: Long-poll loop — blocks on server until new messages arrive
      while (!cancelled) {
        currentAbort = new AbortController();
        try {
          const newMsgs = await nextcloudApi.getMessages(
            selectedRoomToken!,
            { lookIntoFuture: true, lastKnownMessageId: lastMsgIdRef.current, timeout: LONG_POLL_TIMEOUT },
            currentAbort.signal,
          );
          if (cancelled) break;
          if (newMsgs.length > 0) {
            // Separate reaction events from real messages
            const reactionEvents = newMsgs.filter(
              (m) => m.systemMessage === 'reaction' || m.systemMessage === 'reaction_deleted',
            );
            const displayMsgs = newMsgs.filter(
              (m) => m.systemMessage !== 'reaction' && m.systemMessage !== 'reaction_deleted',
            );

            // Advance cursor using ALL messages (including reaction events)
            lastMsgIdRef.current = Math.max(...newMsgs.map(m => m.id));

            // Refresh reaction counts for affected messages
            const affectedIds = new Set<number>();
            for (const rm of reactionEvents) {
              const parentId = parseInt(rm.messageParameters?.message?.id ?? '0', 10);
              if (parentId > 0) affectedIds.add(parentId);
            }
            affectedIds.forEach((id) => fetchReactions(id));

            if (displayMsgs.length > 0) {
              setMessages(prev => [...prev, ...displayMsgs]);
              queryClient.invalidateQueries({ queryKey: ['nextcloud', 'rooms'] });
            }
          }
        } catch (err: any) {
          if (cancelled) break;
          if (err?.code === 'ERR_CANCELED' || err?.name === 'CanceledError') break;
          // Brief backoff before retrying on error
          await new Promise(r => setTimeout(r, 3000));
        }
      }
    }

    run();
    return () => {
      cancelled = true;
      currentAbort?.abort();
    };
  }, [selectedRoomToken, isActive, queryClient, fetchReactions]);

  const room = rooms.find((r) => r.token === selectedRoomToken);
  const isOneToOne = room?.type === 1;

  const sendMutation = useMutation({
    mutationFn: ({ message, replyTo }: { message: string; replyTo?: number }) =>
      nextcloudApi.sendMessage(selectedRoomToken!, message, replyTo),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['nextcloud', 'rooms'] });
    },
  });

  // Mark room as read when first opened
  useEffect(() => {
    if (selectedRoomToken && isActive) {
      nextcloudApi.markAsRead(selectedRoomToken).then(() => {
        queryClient.invalidateQueries({ queryKey: ['nextcloud', 'rooms'] });
      }).catch(() => {});
    }
  }, [selectedRoomToken, isActive, queryClient]);

  // Smart scroll: instant on initial load, smooth only when user is near bottom
  useEffect(() => {
    if (!messagesEndRef.current) return;
    if (isInitialLoadRef.current) {
      messagesEndRef.current.scrollIntoView();
      if (messages.length > 0) isInitialLoadRef.current = false;
      return;
    }
    const container = scrollContainerRef.current;
    if (container) {
      const { scrollHeight, scrollTop, clientHeight } = container;
      const isNearBottom = scrollHeight - scrollTop - clientHeight < 150;
      if (isNearBottom) {
        messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
      }
    }
  }, [messages]);

  const handleSend = () => {
    const trimmed = input.trim();
    if (!trimmed || !selectedRoomToken) return;
    sendMutation.mutate({ message: trimmed, replyTo: replyToMessage?.id });
    setInput('');
    setReplyToMessage(null);
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  const handleReactionToggled = (messageId: number) => {
    fetchReactions(messageId);
  };

  const handleUploadFile = async (file: File) => {
    if (!selectedRoomToken) return;
    if (file.size > 50 * 1024 * 1024) {
      return;
    }
    setUploadProgress(0);
    try {
      await nextcloudApi.uploadFile(selectedRoomToken, file, setUploadProgress);
    } finally {
      setUploadProgress(null);
      if (fileInputRef.current) fileInputRef.current.value = '';
    }
  };

  const handleFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) handleUploadFile(file);
  };

  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragOver(true);
  };

  const handleDragLeave = () => setIsDragOver(false);

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragOver(false);
    const file = e.dataTransfer.files?.[0];
    if (file) handleUploadFile(file);
  };

  if (!selectedRoomToken) {
    return (
      <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }}>
        <Typography color="text.secondary" variant="body2">
          Raum ausw\u00E4hlen
        </Typography>
      </Box>
    );
  }

  return (
    <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      <Box sx={{ px: 1.5, py: 1, borderBottom: 1, borderColor: 'divider', display: 'flex', alignItems: 'center', gap: 1 }}>
        <IconButton size="small" onClick={() => selectRoom(null)}>
          <ArrowBackIcon fontSize="small" />
        </IconButton>
        <Typography variant="subtitle2" noWrap>
          {room?.displayName ?? selectedRoomToken}
        </Typography>
      </Box>

      <Box
        ref={scrollContainerRef}
        sx={{
          flex: 1,
          overflow: 'auto',
          py: 1,
          outline: isDragOver ? '2px dashed' : 'none',
          outlineColor: 'primary.main',
          outlineOffset: '-4px',
          transition: 'outline 0.1s',
        }}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDrop}
      >
        {isLoading ? (
          <Box sx={{ display: 'flex', justifyContent: 'center', pt: 2 }}>
            <CircularProgress size={24} />
          </Box>
        ) : (
          messages.map((msg, idx) => {
            const showDaySep = idx === 0 || dayKey(msg.timestamp) !== dayKey(messages[idx - 1].timestamp);
            return (
              <React.Fragment key={msg.id}>
                {showDaySep && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, px: 2, py: 1 }}>
                    <Divider sx={{ flex: 1 }} />
                    <Chip label={dayLabel(msg.timestamp)} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
                    <Divider sx={{ flex: 1 }} />
                  </Box>
                )}
                <ChatMessage
                  message={msg}
                  isOwnMessage={msg.actorType === 'users' && msg.actorId === loginName}
                  isOneToOne={isOneToOne}
                  onReplyClick={setReplyToMessage}
                  onReactionToggled={handleReactionToggled}
                  reactionsOverride={reactionsMap.get(msg.id)}
                />
              </React.Fragment>
            );
          })
        )}
        <div ref={messagesEndRef} />
      </Box>

      <Box sx={{ p: 1, borderTop: 1, borderColor: 'divider' }}>
        {replyToMessage && (
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, px: 1, py: 0.5, bgcolor: 'action.hover', borderRadius: 1, mb: 0.5 }}>
            <Box sx={{ flex: 1, minWidth: 0 }}>
              <Typography variant="caption" sx={{ fontWeight: 600, display: 'block' }}>{replyToMessage.actorDisplayName}</Typography>
              <Typography variant="caption" noWrap sx={{ display: 'block', opacity: 0.7 }}>{replyToMessage.message}</Typography>
            </Box>
            <IconButton size="small" onClick={() => setReplyToMessage(null)}>
              <CloseIcon fontSize="small" />
            </IconButton>
          </Box>
        )}
        {uploadProgress !== null && (
          <LinearProgress variant="determinate" value={uploadProgress} sx={{ mb: 0.5, borderRadius: 1 }} />
        )}
        <Box sx={{ display: 'flex', gap: 0.5 }}>
          <input
            type="file"
            ref={fileInputRef}
            style={{ display: 'none' }}
            onChange={handleFileInputChange}
          />
          <Tooltip title="Datei anh\u00E4ngen">
            <span>
              <IconButton
                size="small"
                onClick={() => fileInputRef.current?.click()}
                disabled={uploadProgress !== null || !selectedRoomToken}
              >
                <AttachFileIcon fontSize="small" />
              </IconButton>
            </span>
          </Tooltip>
          <TextField
            size="small"
            fullWidth
            placeholder="Nachricht..."
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyDown={handleKeyDown}
            multiline
            maxRows={3}
          />
          <IconButton
            color="primary"
            onClick={handleSend}
            disabled={!input.trim() || sendMutation.isPending}
          >
            <SendIcon />
          </IconButton>
        </Box>
      </Box>
    </Box>
  );
};

export default ChatMessageView;
