import type { UserProfile } from '@/app/components/workflow/comment/types'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { useState } from 'react'
import { MentionInput } from './mention-input'

const mockFetchMentionableUsers = vi.hoisted(() => vi.fn())
const mockSetMentionableUsersLoading = vi.hoisted(() => vi.fn())
const mockSetMentionableUsersCache = vi.hoisted(() => vi.fn())

const mentionStoreState = vi.hoisted(() => ({
  mentionableUsersCache: {} as Record<string, UserProfile[]>,
  mentionableUsersLoading: {} as Record<string, boolean>,
  setMentionableUsersLoading: (appId: string, loading: boolean) => {
    mockSetMentionableUsersLoading(appId, loading)
    mentionStoreState.mentionableUsersLoading[appId] = loading
  },
  setMentionableUsersCache: (appId: string, users: UserProfile[]) => {
    mockSetMentionableUsersCache(appId, users)
    mentionStoreState.mentionableUsersCache[appId] = users
  },
}))
vi.mock('@/next/navigation', () => ({
  useParams: () => ({ appId: 'app-1' }),
}))

vi.mock('@/service/client', () => ({
  consoleClient: {
    apps: {
      byAppId: {
        workflow: {
          comments: {
            mentionUsers: {
              get: (...args: unknown[]) => mockFetchMentionableUsers(...args),
            },
          },
        },
      },
    },
  },
}))

vi.mock('../store', () => ({
  useStore: (selector: (state: typeof mentionStoreState) => unknown) => selector(mentionStoreState),
  useWorkflowStore: () => ({
    getState: () => mentionStoreState,
  }),
}))

vi.mock('@langgenius/dify-ui/avatar', () => ({
  Avatar: ({ name }: { name: string }) => <div data-testid="mention-avatar">{name}</div>,
}))

const mentionUsers: UserProfile[] = [
  {
    id: 'user-2',
    name: 'Alice',
    email: 'alice@example.com',
    avatar_url: 'alice.png',
  },
  {
    id: 'user-3',
    name: 'Bob',
    email: 'bob@example.com',
    avatar_url: 'bob.png',
  },
]

function ControlledMentionInput({
  onSubmit,
}: {
  onSubmit: (content: string, mentionedUserIds: string[]) => void
}) {
  const [value, setValue] = useState('')
  return <MentionInput value={value} onChange={setValue} onSubmit={onSubmit} />
}

describe('MentionInput', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mentionStoreState.mentionableUsersCache = {}
    mentionStoreState.mentionableUsersLoading = {}
    mockFetchMentionableUsers.mockResolvedValue({ users: mentionUsers })
  })

  it('loads mentionable users when cache is empty', async () => {
    render(<MentionInput value="" onChange={vi.fn()} onSubmit={vi.fn()} />)

    await waitFor(() => {
      expect(mockFetchMentionableUsers).toHaveBeenCalledWith({
        params: { app_id: 'app-1' },
      })
    })

    expect(mockSetMentionableUsersLoading).toHaveBeenCalledWith('app-1', true)
    expect(mockSetMentionableUsersCache).toHaveBeenCalledWith('app-1', mentionUsers)
    expect(mockSetMentionableUsersLoading).toHaveBeenCalledWith('app-1', false)
  })

  it('selects a mention and submits with mentioned user ids', async () => {
    mentionStoreState.mentionableUsersCache['app-1'] = mentionUsers
    const onSubmit = vi.fn()

    render(<ControlledMentionInput onSubmit={onSubmit} />)

    const textarea = screen.getByPlaceholderText(
      'workflow.comments.placeholder.add',
    ) as HTMLTextAreaElement
    textarea.focus()
    textarea.setSelectionRange(4, 4)
    fireEvent.change(textarea, { target: { value: '@Ali' } })

    await waitFor(() => {
      expect(screen.getByText('alice@example.com')).toBeInTheDocument()
    })

    fireEvent.click(screen.getByText('alice@example.com'))
    fireEvent.change(textarea, { target: { value: '@Alice hi' } })
    fireEvent.keyDown(textarea, { key: 'Enter' })

    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith('@Alice hi', ['user-2'])
    })
  })

  it('supports editing mode cancel and save actions', async () => {
    mentionStoreState.mentionableUsersCache['app-1'] = mentionUsers
    const onSubmit = vi.fn()
    const onCancel = vi.fn()

    render(
      <MentionInput
        value="  updated reply  "
        onChange={vi.fn()}
        onSubmit={onSubmit}
        onCancel={onCancel}
        isEditing
      />,
    )

    fireEvent.click(screen.getByText('common.operation.cancel'))
    expect(onCancel).toHaveBeenCalledTimes(1)

    fireEvent.click(screen.getByText('common.operation.save'))
    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith('updated reply', [])
    })
  })

  it('focuses the textarea at the end when autoFocus is enabled', () => {
    vi.useFakeTimers()
    try {
      mentionStoreState.mentionableUsersCache['app-1'] = mentionUsers

      const { unmount } = render(
        <MentionInput value="draft" onChange={vi.fn()} onSubmit={vi.fn()} autoFocus />,
      )

      const textarea = screen.getByPlaceholderText(
        'workflow.comments.placeholder.add',
      ) as HTMLTextAreaElement

      act(() => {
        vi.runOnlyPendingTimers()
      })

      expect(document.activeElement).toBe(textarea)
      expect(textarea.selectionStart).toBe(5)
      expect(textarea.selectionEnd).toBe(5)

      unmount()
    } finally {
      vi.useRealTimers()
    }
  })
})
