import type { KeyboardEventHandler } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { socket } from '@/socket/socket.ts';
import { File, Paperclip, Send, X } from 'lucide-react';
import LoadingWheel from '@/components/chat/LoadingWheel.tsx';
import { FileWithPreviewProps } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts';
import { axiosClient } from '@/utils/axiosClient.ts';

export type InputProps = {
  message: string;
  attachments: FileList | null;
};

const MessageForm = () => {
  const { currentContact } = useChat();
  const [files, setFiles] = useState<FileWithPreviewProps[]>([]);
  const [isUploading, setIsUploading] = useState(false);
  const [isSending, setIsSending] = useState<boolean>(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const { register, handleSubmit, reset, watch, setValue } =
    useForm<InputProps>({
      mode: 'onChange',
    });

  const message = watch('message', '');
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);

  // Function to adjust textarea height
  const adjustHeight = useCallback(() => {
    const textarea = textareaRef.current;
    if (textarea) {
      textarea.style.height = 'auto';
      textarea.style.height = `${textarea.scrollHeight}px`;
    }
  }, []);

  useEffect(() => {
    adjustHeight();
  }, [message, adjustHeight]);

  useEffect(() => {
    return () => {
      files.forEach((file) => {
        if (file.preview) {
          URL.revokeObjectURL(file.preview);
        }
      });
    };
  }, [files]);

  const handleClearFiles = () => {
    if (fileInputRef.current) {
      fileInputRef.current.value = '';
    }
    setFiles([]);
    setValue('attachments', null);
  };

  const handleClearSingleFile = (index: number) => {
    setFiles((prevFiles) => {
      const newFiles = [...prevFiles];
      if (newFiles[index].preview) {
        URL.revokeObjectURL(newFiles[index].preview);
      }
      newFiles.splice(index, 1);
      return newFiles;
    });

    if (files.length === 1 && fileInputRef.current) {
      fileInputRef.current.value = '';
      setValue('attachments', null);
    }
  };

  if (!socket) {
    console.error('Socket not initialized');
    return;
  }

  const uploadFiles = async (filesToUpload: File[]) => {
    const formData = new FormData();
    filesToUpload.forEach((file, index) => {
      formData.append(`attachments[${index}]`, file);
    });

    try {
      setIsUploading(true);
      const response = await axiosClient.post(
        '/api/chat/attachments',
        formData,
        {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        },
      );
      setIsUploading(false);
      console.error(response.data);
      if (!response.data) {
        setErrorMessage('Failed to upload attachments');
        return console.error('Failed to upload attachments');
      }

      return response.data;
    } catch (e) {
      setIsUploading(false);
      console.error('Failed to upload attachments: ', e);
      return null;
    }
  };

  const submitMessage: SubmitHandler<InputProps> = async (data) => {
    if ((data.message.trim()?.length < 1 && files.length === 0) || isSending)
      return;
    setErrorMessage(null);
    setIsSending(true);
    if (!socket) {
      console.error('Socket not initialized');
      return;
    }

    let attachmentUrls: string[] = [];
    if (files.length > 0) {
      try {
        const response = await uploadFiles(files.map((f) => f.file));
        console.log(response);
        attachmentUrls = response;
      } catch {
        console.error('Failed to upload attachments');
      }
    }

    // Emit message to server
    socket.emit(
      'chat message',
      {
        message: data.message.trim(),
        recipient: currentContact?.conversation_id,
        attachment_urls: attachmentUrls,
        recipient_id: currentContact?.user_id,
      },
      (response: { status: string; message: string }) => {
        if (response.status === 'ok') {
          setIsSending(false);
          reset({ message: '' });
          handleClearFiles();
        } else {
          setIsSending(false);
          setErrorMessage(response.message);
        }
        console.log('Response: ', response);
      },
    );
    console.log('sent: ', {
      message: data.message.trim(),
      recipient: currentContact?.conversation_id,
      attachment_urls: attachmentUrls,
      recipient_id: currentContact?.user_id,
    });
  };

  const handleKeyPress: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSubmit(submitMessage)();
    }
  };

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) {
      const newFiles: FileWithPreviewProps[] = Array.from(e.target.files).map(
        (file) => ({
          file,
          preview: file.type.startsWith('image/')
            ? URL.createObjectURL(file)
            : null,
        }),
      );

      setFiles((prevFiles) => [...prevFiles, ...newFiles]);
      setValue('attachments', e.target.files);
    }
  };

  const { ref, ...messageRegister } = register('message', {
    maxLength: {
      value: 2000,
      message: 'Maximum length exceeded',
    },
    minLength: 1,
  });

  const charCount = message.length;
  const isNearLimit = charCount > 1900;
  const isOverLimit = charCount > 2000;

  return (
    <form onSubmit={handleSubmit(submitMessage)} className="w-full">
      <div className="flex flex-col gap-2 w-full">
        {files.length > 0 && (
          <div className="flex flex-col gap-2 p-2 bg-[#121212] rounded-md mx-2 border-2">
            <div className="flex items-center justify-between">
              <span className="text-sm text-gray-400">
                {files.length} {files.length === 1 ? 'file' : 'files'} selected
              </span>
              <button
                type="button"
                onClick={handleClearFiles}
                className="p-1 hover:bg-green-100 rounded-full"
                aria-label="Clear all files"
              >
                <X className="w-4 h-4 text-gray-500" />
              </button>
            </div>
            <div className="flex flex-wrap gap-2">
              {files.map((file, index) => (
                <div key={index} className="relative">
                  <div className="flex flex-col p-2 bg-[#1a1a1a] rounded-md">
                    {file.preview ? (
                      <img
                        src={file.preview}
                        alt={`attachment ${index + 1}`}
                        className="rounded-md max-w-full max-h-24 object-contain"
                      />
                    ) : (
                      <div className="flex items-center justify-center text-center p-2">
                        <File className=" text-gray-300" />
                        <p className="text-sm">{file.file.name}</p>
                      </div>
                    )}
                    <span className="text-xs text-gray-400 mt-1">
                      ({Math.round(file.file.size / 1024)} KB)
                    </span>
                    <button
                      type="button"
                      onClick={() => handleClearSingleFile(index)}
                      className="absolute -top-2 -right-2 p-1 bg-gray-800 hover:bg-gray-700 rounded-full"
                      aria-label={`Remove ${file.file.name}`}
                    >
                      <X className="w-3 h-3 text-gray-300" />
                    </button>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        <div className="flex items-end gap-2 w-full text-center">
          <div className="flex-1 inline-block">
            <div className="relative flex justify-center">
              <textarea
                {...messageRegister}
                ref={(e) => {
                  ref(e);
                  textareaRef.current = e;
                }}
                className={` ml-2 w-full overflow-y-hidden resize-none bg-zinc-900 rounded-lg text-white min-h-[40px] max-h-96 placeholder:text-gray-400 focus:border-1 focus:ring-0 focus:border-emerald-800
                ${isOverLimit ? 'border-2 border-red-500' : isNearLimit ? 'border-2 border-yellow-500' : ''} mx-auto`}
                autoFocus={!!currentContact}
                disabled={!currentContact}
                placeholder="Enter message"
                onKeyDown={handleKeyPress}
                rows={1}
              />
              <span
                className={`absolute right-2 bottom-1 text-sm text-gray-400 ${
                  isOverLimit
                    ? 'text-red-500 font-bold'
                    : isNearLimit
                      ? 'text-yellow-600'
                      : 'text-gray-200'
                }`}
              >
                {charCount}/2000
              </span>
            </div>
          </div>
          {errorMessage ? <p className="text-red-200">{errorMessage}</p> : null}
          <div className="flex">
            <label
              htmlFor="attachments"
              className="flex items-center justify-center hover:cursor-pointer p-2 rounded-full hover:bg-zinc-800"
            >
              <Paperclip className="text-gray-200" />
            </label>
            <input
              name="attachments"
              id="attachments"
              type="file"
              accept="*/*"
              onChange={handleFileChange}
              disabled={isUploading}
              ref={fileInputRef}
              className="hidden"
              multiple={true}
            />
            {isUploading && (
              <span className="text-gray-500 text-sm">Uploading...</span>
            )}
            <button
              className="text-white hover:bg-zinc-800 rounded-full flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed h-10 w-10"
              type="submit"
              disabled={isOverLimit || isSending || isUploading}
            >
              {isSending ? (
                <LoadingWheel />
              ) : (
                <Send className="text-gray-200" />
              )}
            </button>
          </div>
        </div>
      </div>
    </form>
  );
};

export default MessageForm;
