import type { UserProfile, WorkflowCommentDetail, WorkflowCommentList } from '@/service/workflow-comment'
import { useParams } from 'next/navigation'
import { useCallback, useEffect, useRef } from 'react'
import { useReactFlow } from 'reactflow'
import { collaborationManager } from '@/app/components/workflow/collaboration'
import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { createWorkflowComment, createWorkflowCommentReply, deleteWorkflowComment, deleteWorkflowCommentReply, fetchWorkflowComment, fetchWorkflowComments, resolveWorkflowComment, updateWorkflowComment, updateWorkflowCommentReply } from '@/service/workflow-comment'
import { useStore } from '../store'
import { ControlMode } from '../types'

const EMPTY_USERS: UserProfile[] = []
type CommentDetailResponse = WorkflowCommentDetail | { data: WorkflowCommentDetail }

const getCommentDetail = (response: CommentDetailResponse): WorkflowCommentDetail => {
  if ('data' in response)
    return response.data
  return response
}

export const useWorkflowComment = () => {
  const params = useParams()
  const appId = params.appId as string
  const reactflow = useReactFlow()
  const controlMode = useStore(s => s.controlMode)
  const pendingComment = useStore(s => s.pendingComment)
  const setPendingComment = useStore(s => s.setPendingComment)
  const isCommentQuickAdd = useStore(s => s.isCommentQuickAdd)
  const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
  const isCommentPlacing = useStore(s => s.isCommentPlacing)
  const setActiveCommentId = useStore(s => s.setActiveCommentId)
  const activeCommentId = useStore(s => s.activeCommentId)
  const comments = useStore(s => s.comments)
  const setComments = useStore(s => s.setComments)
  const loading = useStore(s => s.commentsLoading)
  const setCommentsLoading = useStore(s => s.setCommentsLoading)
  const activeComment = useStore(s => s.activeCommentDetail)
  const setActiveComment = useStore(s => s.setActiveCommentDetail)
  const activeCommentLoading = useStore(s => s.activeCommentDetailLoading)
  const setActiveCommentLoading = useStore(s => s.setActiveCommentDetailLoading)
  const replySubmitting = useStore(s => s.replySubmitting)
  const setReplySubmitting = useStore(s => s.setReplySubmitting)
  const replyUpdating = useStore(s => s.replyUpdating)
  const setReplyUpdating = useStore(s => s.setReplyUpdating)
  const commentDetailCache = useStore(s => s.commentDetailCache)
  const setCommentDetailCache = useStore(s => s.setCommentDetailCache)
  const rightPanelWidth = useStore(s => s.rightPanelWidth)
  const nodePanelWidth = useStore(s => s.nodePanelWidth)
  const mentionableUsers = useStore(state => (
    appId ? state.mentionableUsersCache[appId] ?? EMPTY_USERS : EMPTY_USERS
  ))
  const { userProfile } = useAppContext()
  const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
  const commentDetailCacheRef = useRef<Record<string, WorkflowCommentDetail>>(commentDetailCache)
  const activeCommentIdRef = useRef<string | null>(null)

  useEffect(() => {
    activeCommentIdRef.current = activeCommentId ?? null
  }, [activeCommentId])

  useEffect(() => {
    commentDetailCacheRef.current = commentDetailCache
  }, [commentDetailCache])

  const refreshActiveComment = useCallback(async (commentId: string) => {
    if (!appId)
      return

    const detailResponse = await fetchWorkflowComment(appId, commentId) as CommentDetailResponse
    const detail = getCommentDetail(detailResponse)

    commentDetailCacheRef.current = {
      ...commentDetailCacheRef.current,
      [commentId]: detail,
    }
    setCommentDetailCache(commentDetailCacheRef.current)
    setActiveComment(detail)
  }, [appId, setActiveComment, setCommentDetailCache])

  const loadComments = useCallback(async () => {
    if (!appId || !isCollaborationEnabled)
      return

    setCommentsLoading(true)
    try {
      const commentsData = await fetchWorkflowComments(appId)
      setComments(commentsData)
    }
    catch (error) {
      console.error('Failed to fetch comments:', error)
    }
    finally {
      setCommentsLoading(false)
    }
  }, [appId, isCollaborationEnabled, setComments, setCommentsLoading])

  // Setup collaboration
  useEffect(() => {
    if (!appId || !isCollaborationEnabled)
      return

    const unsubscribe = collaborationManager.onCommentsUpdate(() => {
      loadComments()
      if (activeCommentIdRef.current)
        refreshActiveComment(activeCommentIdRef.current)
    })

    return unsubscribe
  }, [appId, isCollaborationEnabled, loadComments, refreshActiveComment])

  useEffect(() => {
    loadComments()
  }, [loadComments])

  const handleCommentSubmit = useCallback(async (content: string, mentionedUserIds: string[] = []) => {
    if (!pendingComment)
      return

    if (!appId) {
      console.error('AppId is missing')
      return
    }

    try {
      // Convert screen position to flow position when submitting
      const { screenToFlowPosition } = reactflow
      const flowPosition = screenToFlowPosition({
        x: pendingComment.pageX,
        y: pendingComment.pageY,
      })

      const newComment = await createWorkflowComment(appId, {
        position_x: flowPosition.x,
        position_y: flowPosition.y,
        content,
        mentioned_user_ids: mentionedUserIds,
      })

      const createdAt = Number(newComment.created_at)
      const createdAtSeconds = Number.isNaN(createdAt)
        ? Math.floor(Date.parse(newComment.created_at) / 1000)
        : createdAt
      const createdByAccount = {
        id: userProfile?.id ?? '',
        name: userProfile?.name ?? '',
        email: userProfile?.email ?? '',
        avatar_url: userProfile?.avatar_url || userProfile?.avatar || undefined,
      }
      const mentionedUsers = mentionedUserIds
        .map(mentionedId => mentionableUsers.find(user => user.id === mentionedId))
        .filter((user): user is NonNullable<typeof user> => Boolean(user))
      const uniqueParticipantsMap = new Map<string, typeof createdByAccount>()
      if (createdByAccount.id)
        uniqueParticipantsMap.set(createdByAccount.id, createdByAccount)
      for (const user of mentionedUsers) {
        if (!uniqueParticipantsMap.has(user.id)) {
          uniqueParticipantsMap.set(user.id, {
            id: user.id,
            name: user.name,
            email: user.email,
            avatar_url: user.avatar_url,
          })
        }
      }
      const participants = Array.from(uniqueParticipantsMap.values())

      const composedComment: WorkflowCommentList = {
        id: newComment.id,
        position_x: flowPosition.x,
        position_y: flowPosition.y,
        content,
        created_by: createdByAccount.id,
        created_by_account: createdByAccount,
        created_at: createdAtSeconds,
        updated_at: createdAtSeconds,
        resolved: false,
        mention_count: mentionedUserIds.length,
        reply_count: 0,
        participants,
      }

      const composedDetail: WorkflowCommentDetail = {
        id: newComment.id,
        position_x: flowPosition.x,
        position_y: flowPosition.y,
        content,
        created_by: createdByAccount.id,
        created_by_account: createdByAccount,
        created_at: createdAtSeconds,
        updated_at: createdAtSeconds,
        resolved: false,
        replies: [],
        mentions: mentionedUserIds.map(mentionedId => ({
          mentioned_user_id: mentionedId,
          mentioned_user_account: mentionableUsers.find(user => user.id === mentionedId) ?? null,
          reply_id: null,
        })),
      }

      setComments([...comments, composedComment])
      commentDetailCacheRef.current = {
        ...commentDetailCacheRef.current,
        [newComment.id]: composedDetail,
      }
      setCommentDetailCache(commentDetailCacheRef.current)

      collaborationManager.emitCommentsUpdate(appId)

      setPendingComment(null)
      setCommentQuickAdd(false)
    }
    catch (error) {
      console.error('Failed to create comment:', error)
      setPendingComment(null)
      setCommentQuickAdd(false)
    }
  }, [appId, pendingComment, setPendingComment, setCommentQuickAdd, reactflow, comments, setComments, userProfile, setCommentDetailCache, mentionableUsers])

  const handleCommentCancel = useCallback(() => {
    setPendingComment(null)
    setCommentQuickAdd(false)
  }, [setPendingComment, setCommentQuickAdd])

  useEffect(() => {
    if (controlMode !== ControlMode.Comment && !isCommentQuickAdd)
      setPendingComment(null)
  }, [controlMode, isCommentQuickAdd, setPendingComment])

  useEffect(() => {
    if (!pendingComment && !isCommentPlacing && isCommentQuickAdd)
      setCommentQuickAdd(false)
  }, [isCommentPlacing, isCommentQuickAdd, pendingComment, setCommentQuickAdd])

  const handleCommentIconClick = useCallback(async (comment: WorkflowCommentList) => {
    setPendingComment(null)

    activeCommentIdRef.current = comment.id
    setActiveCommentId(comment.id)

    const cachedDetail = commentDetailCacheRef.current[comment.id]
    setActiveComment(cachedDetail || comment)

    const hasSelectedNode = reactflow.getNodes().some(node => node.data?.selected)
    const commentPanelWidth = controlMode === ControlMode.Comment ? 420 : 0
    const fallbackPanelWidth = (hasSelectedNode ? nodePanelWidth : 0) + commentPanelWidth
    const effectivePanelWidth = Math.max(rightPanelWidth ?? 0, fallbackPanelWidth)

    const baseHorizontalOffsetPx = 220
    const panelCompensationPx = effectivePanelWidth / 2
    const desiredHorizontalOffsetPx = baseHorizontalOffsetPx + panelCompensationPx
    const maxOffset = Math.max(0, (window.innerWidth / 2) - 60)
    const horizontalOffsetPx = Math.min(desiredHorizontalOffsetPx, maxOffset)

    reactflow.setCenter(
      comment.position_x + horizontalOffsetPx,
      comment.position_y,
      { zoom: 1, duration: 600 },
    )

    if (!appId)
      return

    setActiveCommentLoading(!cachedDetail)

    try {
      const detailResponse = await fetchWorkflowComment(appId, comment.id) as CommentDetailResponse
      const detail = getCommentDetail(detailResponse)

      commentDetailCacheRef.current = {
        ...commentDetailCacheRef.current,
        [comment.id]: detail,
      }
      setCommentDetailCache(commentDetailCacheRef.current)

      if (activeCommentIdRef.current === comment.id)
        setActiveComment(detail)
    }
    catch (e) {
      console.warn('Failed to load workflow comment detail', e)
    }
    finally {
      setActiveCommentLoading(false)
    }
  }, [
    appId,
    controlMode,
    nodePanelWidth,
    reactflow,
    rightPanelWidth,
    setActiveComment,
    setActiveCommentId,
    setActiveCommentLoading,
    setCommentDetailCache,
    setPendingComment,
  ])

  const handleCommentResolve = useCallback(async (commentId: string) => {
    if (!appId)
      return

    setActiveCommentLoading(true)
    try {
      await resolveWorkflowComment(appId, commentId)

      collaborationManager.emitCommentsUpdate(appId)

      await refreshActiveComment(commentId)
      await loadComments()
    }
    catch (error) {
      console.error('Failed to resolve comment:', error)
    }
    finally {
      setActiveCommentLoading(false)
    }
  }, [appId, loadComments, refreshActiveComment, setActiveCommentLoading])

  const handleCommentDelete = useCallback(async (commentId: string) => {
    if (!appId)
      return

    setActiveCommentLoading(true)
    try {
      await deleteWorkflowComment(appId, commentId)

      collaborationManager.emitCommentsUpdate(appId)

      const updatedCache = { ...commentDetailCacheRef.current }
      delete updatedCache[commentId]
      commentDetailCacheRef.current = updatedCache
      setCommentDetailCache(updatedCache)

      const currentComments = comments.filter(c => c.id !== commentId)
      const commentIndex = comments.findIndex(c => c.id === commentId)
      const fallbackTarget = commentIndex >= 0 ? comments[commentIndex + 1] ?? comments[commentIndex - 1] : undefined

      await loadComments()

      if (fallbackTarget) {
        handleCommentIconClick(fallbackTarget)
      }
      else if (currentComments.length > 0) {
        const nextComment = currentComments[0]
        handleCommentIconClick(nextComment)
      }
      else {
        setActiveComment(null)
        setActiveCommentId(null)
        activeCommentIdRef.current = null
      }
    }
    catch (error) {
      console.error('Failed to delete comment:', error)
    }
    finally {
      setActiveCommentLoading(false)
    }
  }, [appId, comments, handleCommentIconClick, loadComments, setActiveComment, setActiveCommentId, setActiveCommentLoading, setCommentDetailCache])

  const handleCommentPositionUpdate = useCallback(async (commentId: string, position: { x: number, y: number }) => {
    if (!appId)
      return

    const targetComment = comments.find(c => c.id === commentId)
    if (!targetComment)
      return

    const nextPosition = {
      position_x: position.x,
      position_y: position.y,
    }

    const previousComments = comments
    const updatedComments = comments.map(c =>
      c.id === commentId
        ? { ...c, ...nextPosition }
        : c,
    )
    setComments(updatedComments)

    const cachedDetail = commentDetailCacheRef.current[commentId]
    const updatedDetail = cachedDetail ? { ...cachedDetail, ...nextPosition } : null
    if (updatedDetail) {
      commentDetailCacheRef.current = {
        ...commentDetailCacheRef.current,
        [commentId]: updatedDetail,
      }
      setCommentDetailCache(commentDetailCacheRef.current)

      if (activeCommentIdRef.current === commentId)
        setActiveComment(updatedDetail)
    }
    else if (activeComment?.id === commentId) {
      setActiveComment({ ...activeComment, ...nextPosition })
    }

    try {
      await updateWorkflowComment(appId, commentId, {
        content: targetComment.content,
        position_x: nextPosition.position_x,
        position_y: nextPosition.position_y,
      })
      collaborationManager.emitCommentsUpdate(appId)
    }
    catch (error) {
      console.error('Failed to update comment position:', error)
      setComments(previousComments)

      if (cachedDetail) {
        commentDetailCacheRef.current = {
          ...commentDetailCacheRef.current,
          [commentId]: cachedDetail,
        }
        setCommentDetailCache(commentDetailCacheRef.current)

        if (activeCommentIdRef.current === commentId)
          setActiveComment(cachedDetail)
      }
      else if (activeComment?.id === commentId) {
        setActiveComment(activeComment)
      }
    }
  }, [activeComment, appId, comments, setComments, setCommentDetailCache, setActiveComment])

  const handleCommentReply = useCallback(async (commentId: string, content: string, mentionedUserIds: string[] = []) => {
    if (!appId)
      return
    const trimmed = content.trim()
    if (!trimmed)
      return

    setReplySubmitting(true)
    try {
      await createWorkflowCommentReply(appId, commentId, { content: trimmed, mentioned_user_ids: mentionedUserIds })

      collaborationManager.emitCommentsUpdate(appId)

      await refreshActiveComment(commentId)
      await loadComments()
    }
    catch (error) {
      console.error('Failed to create reply:', error)
    }
    finally {
      setReplySubmitting(false)
    }
  }, [appId, loadComments, refreshActiveComment, setReplySubmitting])

  const handleCommentReplyUpdate = useCallback(async (commentId: string, replyId: string, content: string, mentionedUserIds: string[] = []) => {
    if (!appId)
      return
    const trimmed = content.trim()
    if (!trimmed)
      return

    setReplyUpdating(true)
    try {
      await updateWorkflowCommentReply(appId, commentId, replyId, { content: trimmed, mentioned_user_ids: mentionedUserIds })

      collaborationManager.emitCommentsUpdate(appId)

      await refreshActiveComment(commentId)
      await loadComments()
    }
    catch (error) {
      console.error('Failed to update reply:', error)
    }
    finally {
      setReplyUpdating(false)
    }
  }, [appId, loadComments, refreshActiveComment, setReplyUpdating])

  const handleCommentReplyDelete = useCallback(async (commentId: string, replyId: string) => {
    if (!appId)
      return

    setActiveCommentLoading(true)
    try {
      await deleteWorkflowCommentReply(appId, commentId, replyId)

      collaborationManager.emitCommentsUpdate(appId)

      await refreshActiveComment(commentId)
      await loadComments()
    }
    catch (error) {
      console.error('Failed to delete reply:', error)
    }
    finally {
      setActiveCommentLoading(false)
    }
  }, [appId, loadComments, refreshActiveComment, setActiveCommentLoading])

  const handleCommentNavigate = useCallback((direction: 'prev' | 'next') => {
    const currentId = activeCommentIdRef.current
    if (!currentId)
      return
    const idx = comments.findIndex(c => c.id === currentId)
    if (idx === -1)
      return
    const target = direction === 'prev' ? comments[idx - 1] : comments[idx + 1]
    if (target)
      handleCommentIconClick(target)
  }, [comments, handleCommentIconClick])

  const handleActiveCommentClose = useCallback(() => {
    setActiveComment(null)
    setActiveCommentLoading(false)
    setActiveCommentId(null)
    activeCommentIdRef.current = null
  }, [setActiveComment, setActiveCommentId, setActiveCommentLoading])

  const handleCreateComment = useCallback((mousePosition: {
    pageX: number
    pageY: number
    elementX: number
    elementY: number
  }) => {
    if (controlMode === ControlMode.Comment)
      setPendingComment(mousePosition)
  }, [controlMode, setPendingComment])

  return {
    comments,
    loading,
    pendingComment,
    activeComment,
    activeCommentLoading,
    replySubmitting,
    replyUpdating,
    handleCommentSubmit,
    handleCommentCancel,
    handleCommentIconClick,
    handleActiveCommentClose,
    handleCommentResolve,
    handleCommentDelete,
    handleCommentNavigate,
    handleCommentReply,
    handleCommentReplyUpdate,
    handleCommentReplyDelete,
    handleCommentPositionUpdate,
    refreshActiveComment,
    handleCreateComment,
    loadComments,
  }
}
