import type { UserProfile, WorkflowCommentDetail, WorkflowCommentList } from '@/contract/console/workflow-comment'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useReactFlow } from 'reactflow'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
import { useAppContext } from '@/context/app-context'
import { useParams } from '@/next/navigation'
import { consoleClient } from '@/service/client'
import { systemFeaturesQueryOptions } from '@/service/system-features'
import { useStore } from '../store'
import { ControlMode } from '../types'

const EMPTY_USERS: UserProfile[] = []

const normalizeTimestamp = (value: number | string): number => {
  if (typeof value === 'number')
    return value

  const parsed = Number(value)
  if (!Number.isNaN(parsed))
    return parsed

  return Math.floor(Date.parse(value) / 1000)
}

const toCommentDetailPreview = (comment: WorkflowCommentList): WorkflowCommentDetail => ({
  ...comment,
  replies: [],
  mentions: [],
})

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 mentionableUserById = useMemo(
    () => new Map(mentionableUsers.map(user => [user.id, user])),
    [mentionableUsers],
  )
  const { userProfile } = useAppContext()
  const { data: isCollaborationEnabled } = useSuspenseQuery({
    ...systemFeaturesQueryOptions(),
    select: s => s.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 detail = await consoleClient.workflowComments.detail({
      params: { appId, commentId },
    })

    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 response = await consoleClient.workflowComments.list({
        params: { appId },
      })
      setComments(response.data)
    }
    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 consoleClient.workflowComments.create({
        params: { appId },
        body: {
          position_x: flowPosition.x,
          position_y: flowPosition.y,
          content,
          mentioned_user_ids: mentionedUserIds,
        },
      })

      const createdAtSeconds = normalizeTimestamp(newComment.created_at)
      const createdByAccount = {
        id: userProfile?.id ?? '',
        name: userProfile?.name ?? '',
        email: userProfile?.email ?? '',
        avatar_url: userProfile?.avatar_url || userProfile?.avatar || undefined,
      }
      const mentionedUsers = mentionedUserIds
        .map(mentionedId => mentionableUserById.get(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: mentionableUserById.get(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, mentionableUserById])

  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 ?? toCommentDetailPreview(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 detail = await consoleClient.workflowComments.detail({
        params: { appId, commentId: comment.id },
      })

      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 consoleClient.workflowComments.resolve({
        params: { 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 consoleClient.workflowComments.delete({
        params: { 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 consoleClient.workflowComments.update({
        params: { appId, commentId },
        body: {
          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 handleCommentUpdate = useCallback(async (commentId: string, content: string, mentionedUserIds: string[] = []) => {
    if (!appId)
      return
    const trimmed = content.trim()
    if (!trimmed)
      return

    const targetComment = comments.find(c => c.id === commentId)
    const targetDetail = commentDetailCacheRef.current[commentId]
      ?? (activeCommentIdRef.current === commentId ? activeComment : null)
    const positionX = targetDetail?.position_x ?? targetComment?.position_x
    const positionY = targetDetail?.position_y ?? targetComment?.position_y

    if (positionX === undefined || positionY === undefined)
      return

    try {
      await consoleClient.workflowComments.update({
        params: { appId, commentId },
        body: {
          content: trimmed,
          position_x: positionX,
          position_y: positionY,
          mentioned_user_ids: mentionedUserIds,
        },
      })

      collaborationManager.emitCommentsUpdate(appId)

      await refreshActiveComment(commentId)
      await loadComments()
    }
    catch (error) {
      console.error('Failed to update comment:', error)
    }
  }, [activeComment, appId, comments, loadComments, refreshActiveComment])

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

    setReplySubmitting(true)
    try {
      await consoleClient.workflowComments.replies.create({
        params: { appId, commentId },
        body: { 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 consoleClient.workflowComments.replies.update({
        params: { appId, commentId, replyId },
        body: { 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 consoleClient.workflowComments.replies.delete({
        params: { 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,
    handleCommentUpdate,
    handleCommentReply,
    handleCommentReplyUpdate,
    handleCommentReplyDelete,
    handleCommentPositionUpdate,
    refreshActiveComment,
    handleCreateComment,
    loadComments,
  }
}
