"use client";

import { useEffect, useRef, useState, useCallback } from "react";
import { io, Socket } from "socket.io-client";

const API =
  process.env.NEXT_PUBLIC_API_URL?.replace("/api", "") ??
  "http://localhost:3007";

export function getUserId(): number | null {
  try {
    const token = localStorage.getItem("access_token");
    if (!token) return null;
    const payload = JSON.parse(atob(token.split(".")[1]));
    const id = payload?.sub ?? payload?.id ?? payload?.userId;
    return id ? Number(id) : null;
  } catch {
    return null;
  }
}

function getToken(): string {
  return localStorage.getItem("access_token") || "";
}

// ── Notification helpers ──────────────────────────────────────────────────────
function playNotificationSound() {
  try {
    const AC = window.AudioContext || (window as any).webkitAudioContext;
    if (!AC) return;
    const ctx = new AC();
    const o1 = ctx.createOscillator();
    const o2 = ctx.createOscillator();
    const g = ctx.createGain();
    o1.connect(g);
    o2.connect(g);
    g.connect(ctx.destination);
    o1.frequency.value = 880;
    o2.frequency.value = 1175;
    o1.type = "sine";
    o2.type = "sine";
    g.gain.setValueAtTime(0.0001, ctx.currentTime);
    g.gain.exponentialRampToValueAtTime(0.12, ctx.currentTime + 0.02);
    g.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.35);
    o1.start();
    o2.start(ctx.currentTime + 0.08);
    o1.stop(ctx.currentTime + 0.35);
    o2.stop(ctx.currentTime + 0.35);
  } catch {
    /* silent */
  }
}

function showBrowserNotification(title: string, body: string, tag: string) {
  try {
    if (!("Notification" in window)) return;
    if (Notification.permission !== "granted") return;
    if (!document.hidden) return;
    new Notification(title, { body, tag, silent: false });
  } catch {
    /* silent */
  }
}

export interface ChatMessage {
  id: number;
  room_id: number;
  sender_id: number;
  sender_name: string;
  message: string;
  type: "text" | "file" | "image";
  file_url?: string;
  file_name?: string;
  reply_to_id?: number;
  reply_message?: string;
  reply_sender?: string;
  created_at: string;
  pending?: boolean;
}

export interface ChatRoom {
  id: number;
  name: string | null;
  inquiry_id: number | null;
  inquiry_ref: string | null;
  created_at: string;
  last_message: string | null;
  last_sender_id: number | null;
  last_message_at: string | null;
  unread_count: number;
  other_user_id: number | null;
  other_first_name: string | null;
  other_last_name: string | null;
  other_email: string | null;
}

export interface TypingState {
  [roomId: number]: number[];
}
export interface ReadState {
  [roomId: number]: number[];
}

export function useChat() {
  const socketRef = useRef<Socket | null>(null);
  const [connected, setConnected] = useState(false);
  const [rooms, setRooms] = useState<ChatRoom[]>([]);
  const [messages, setMessages] = useState<Record<number, ChatMessage[]>>({});
  const [typing, setTyping] = useState<TypingState>({});
  const [online, setOnline] = useState<Set<number>>(new Set());
  const [totalUnread, setTotalUnread] = useState(0);
  const [readBy, setReadBy] = useState<ReadState>({});
  const [notifications, setNotifications] = useState<ChatMessage[]>([]);
  const typingTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>({});
  const currentUidRef = useRef<number | null>(null);

  const fetchRooms = useCallback(async () => {
    try {
      const res = await fetch(`${API}/api/chat/rooms`, {
        headers: { Authorization: `Bearer ${getToken()}` },
      });
      if (!res.ok) return;
      const data: ChatRoom[] = await res.json();
      setRooms(data);
      setTotalUnread(data.reduce((s, r) => s + Number(r.unread_count ?? 0), 0));
    } catch {
      /**/
    }
  }, []);

  const fetchMessages = useCallback(async (roomId: number) => {
    try {
      const res = await fetch(
        `${API}/api/chat/rooms/${roomId}/messages?limit=100`,
        { headers: { Authorization: `Bearer ${getToken()}` } },
      );
      if (!res.ok) return;
      const data = await res.json();
      const mapped: ChatMessage[] = (data as any[]).map((m) => ({
        id: m.id,
        room_id: m.room_id,
        sender_id: m.sender_id,
        sender_name:
          `${m.sender_first_name ?? ""} ${m.sender_last_name ?? ""}`.trim(),
        message: m.message,
        type: m.type,
        file_url: m.file_url,
        file_name: m.file_name,
        reply_to_id: m.reply_to_id,
        reply_message: m.reply_message,
        reply_sender: m.reply_sender_first,
        created_at: m.created_at,
      }));
      setMessages((prev) => ({ ...prev, [roomId]: mapped }));
    } catch {
      /**/
    }
  }, []);

  const dismissNotification = useCallback((id: number) => {
    setNotifications((prev) => prev.filter((n) => n.id !== id));
  }, []);

  const clearNotifications = useCallback(() => {
    setNotifications([]);
  }, []);

  useEffect(() => {
    const token = getToken();
    if (!token) return;

    currentUidRef.current = getUserId();

    try {
      if ("Notification" in window && Notification.permission === "default") {
        Notification.requestPermission().catch(() => {});
      }
    } catch {
      /* silent */
    }

    const socket = io(`${API}/chat`, {
      auth: { token },
      transports: ["websocket", "polling"],
    });
    socketRef.current = socket;

    socket.on("connect", () => {
      setConnected(true);
      fetchRooms();
    });
    socket.on("disconnect", () => setConnected(false));

    socket.on("new_message", (msg: ChatMessage) => {
      console.log("📨 new_message received:", msg, "myId:", currentUidRef.current);

      setMessages((prev) => {
        const existing = prev[msg.room_id] ?? [];
        const filtered = existing.filter(
          (m) =>
            !(
              m.pending &&
              m.message === msg.message &&
              m.sender_id === msg.sender_id
            ),
        );
        return { ...prev, [msg.room_id]: [...filtered, msg] };
      });
      setRooms((prev) =>
        prev
          .map((r) =>
            r.id === msg.room_id
              ? {
                  ...r,
                  last_message: msg.type === "image" ? "📷 Image" : msg.type === "file" ? "📎 File" : msg.message,
                  last_message_at: msg.created_at,
                  unread_count: r.unread_count + 1,
                }
              : r,
          )
          .sort(
            (a, b) =>
              new Date(b.last_message_at ?? b.created_at).getTime() -
              new Date(a.last_message_at ?? a.created_at).getTime(),
          ),
      );
      setTotalUnread((prev) => prev + 1);

      const myId = currentUidRef.current;
      if (myId && Number(msg.sender_id) !== Number(myId)) {
        setNotifications((prev) => {
          const withoutSameRoom = prev.filter((n) => n.room_id !== msg.room_id);
          return [...withoutSameRoom, msg].slice(-3);
        });
        playNotificationSound();
        const previewText =
          msg.type === "image" ? "📷 Sent an image" :
          msg.type === "file"  ? `📎 ${msg.file_name || 'File'}` :
          msg.message;
        showBrowserNotification(
          msg.sender_name || "New message",
          previewText,
          `chat-room-${msg.room_id}`,
        );
      }
    });

    socket.on(
      "user_typing",
      ({ userId, roomId }: { userId: number; roomId: number }) => {
        setTyping((prev) => ({
          ...prev,
          [roomId]: [...new Set([...(prev[roomId] ?? []), userId])],
        }));
      },
    );
    socket.on(
      "user_stop_typing",
      ({ userId, roomId }: { userId: number; roomId: number }) => {
        setTyping((prev) => ({
          ...prev,
          [roomId]: (prev[roomId] ?? []).filter((id) => id !== userId),
        }));
      },
    );
    socket.on("user_online", ({ userId }: { userId: number }) =>
      setOnline((prev) => new Set([...prev, userId])),
    );
    socket.on("user_offline", ({ userId }: { userId: number }) =>
      setOnline((prev) => {
        const s = new Set(prev);
        s.delete(userId);
        return s;
      }),
    );
    socket.on(
      "messages_read",
      ({ roomId, userId }: { roomId: number; userId: number }) => {
        setReadBy((prev) => ({
          ...prev,
          [roomId]: [...new Set([...(prev[roomId] ?? []), userId])],
        }));
        setRooms((prev) =>
          prev.map((r) => (r.id === roomId ? { ...r, unread_count: 0 } : r)),
        );
      },
    );

    fetchRooms();
    return () => {
      socket.disconnect();
    };
  }, [fetchRooms]);

  const joinRoom = useCallback(
    (roomId: number) => {
      socketRef.current?.emit("join_room", { roomId });
      fetchMessages(roomId);
      setNotifications((prev) => prev.filter((n) => n.room_id !== roomId));
    },
    [fetchMessages],
  );

  const sendMessage = useCallback(
    (roomId: number, message: string, replyToId?: number) => {
      if (!message.trim()) return;
      const uid = getUserId();

      const optimistic: ChatMessage = {
        id: Date.now(),
        room_id: roomId,
        sender_id: uid ?? 0,
        sender_name: "You",
        message,
        type: "text",
        reply_to_id: replyToId,
        created_at: new Date().toISOString(),
        pending: true,
      };
      setMessages((prev) => ({
        ...prev,
        [roomId]: [...(prev[roomId] ?? []), optimistic],
      }));
      socketRef.current?.emit("send_message", {
        roomId,
        message,
        type: "text",
        reply_to_id: replyToId,
      });
    },
    [],
  );

  // ── NEW: Send a file or image message ───────────────────────────────────
  const sendFileMessage = useCallback(
    async (roomId: number, file: File, replyToId?: number): Promise<void> => {
      const uid = getUserId();
      const tempId = Date.now();
      const isImage = file.type.startsWith("image/");

      // Optimistic UI — show file/image bubble immediately with local preview
      const optimistic: ChatMessage = {
        id: tempId,
        room_id: roomId,
        sender_id: uid ?? 0,
        sender_name: "You",
        message: file.name,
        type: isImage ? "image" : "file",
        file_url: URL.createObjectURL(file),
        file_name: file.name,
        reply_to_id: replyToId,
        created_at: new Date().toISOString(),
        pending: true,
      };
      setMessages((prev) => ({
        ...prev,
        [roomId]: [...(prev[roomId] ?? []), optimistic],
      }));

      try {
        const fd = new FormData();
        fd.append("file", file);
        const res = await fetch(`${API}/api/chat/upload`, {
          method: "POST",
          headers: { Authorization: `Bearer ${getToken()}` },
          body: fd,
        });
        if (!res.ok) throw new Error(`Upload failed (${res.status})`);
        const data = await res.json();

        socketRef.current?.emit("send_message", {
          roomId,
          message: data.file_name,
          type: data.type,
          file_url: data.file_url,
          file_name: data.file_name,
          reply_to_id: replyToId,
        });
      } catch (err) {
        console.error("File upload failed:", err);
        setMessages((prev) => ({
          ...prev,
          [roomId]: (prev[roomId] ?? []).filter((m) => m.id !== tempId),
        }));
        alert("Failed to upload file. Please try again.");
      }
    },
    [],
  );

  const sendTyping = useCallback((roomId: number) => {
    socketRef.current?.emit("typing_start", { roomId });
    const key = `${roomId}`;
    clearTimeout(typingTimers.current[key]);
    typingTimers.current[key] = setTimeout(() => {
      socketRef.current?.emit("typing_stop", { roomId });
    }, 2000);
  }, []);

  const markRead = useCallback((roomId: number) => {
    socketRef.current?.emit("mark_read", { roomId });
    setRooms((prev) =>
      prev.map((r) => (r.id === roomId ? { ...r, unread_count: 0 } : r)),
    );
    setTotalUnread((prev) => Math.max(0, prev - 1));
  }, []);

  const createDirectRoom = useCallback(
    async (targetUserId: number): Promise<ChatRoom | null> => {
      try {
        const res = await fetch(
          `${API}/api/chat/rooms/direct/${targetUserId}`,
          {
            method: "POST",
            headers: { Authorization: `Bearer ${getToken()}` },
          },
        );
        if (!res.ok) return null;
        const room = await res.json();
        await fetchRooms();
        return room;
      } catch {
        return null;
      }
    },
    [fetchRooms],
  );

  return {
    connected,
    rooms,
    messages,
    typing,
    online,
    totalUnread,
    readBy,
    getUserId,
    joinRoom,
    sendMessage,
    sendFileMessage,   // ← NEW
    sendTyping,
    markRead,
    fetchMessages,
    fetchRooms,
    createDirectRoom,
    notifications,
    dismissNotification,
    clearNotifications,
  };
}