import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import WorkMemos from './WorkMemos'
import type { Contact, Org, WorkMemo } from '../api/client'

const mocks = vi.hoisted(() => ({
  getMemos: vi.fn(),
  createMemo: vi.fn(),
  deleteMemo: vi.fn(),
}))

vi.mock('../api/client', async (importOriginal) => ({
  ...(await importOriginal<typeof import('../api/client')>()),
  getMemos: mocks.getMemos,
  createMemo: mocks.createMemo,
  deleteMemo: mocks.deleteMemo,
}))

function contact(overrides: Partial<Contact> = {}): Contact {
  return {
    id: 1,
    category: 'APPLICANT',
    name: '이름없음',
    affiliation: null,
    deptName: null,
    title: null,
    phone: null,
    email: null,
    ...overrides,
  }
}

function org(overrides: Partial<Org> = {}): Org {
  return {
    id: 1,
    orgNo: '001',
    orgName: '국제방송교류재단',
    status: 'ACTIVE',
    applicant: contact({
      id: 1,
      category: 'APPLICANT',
      name: '송민지',
      affiliation: '국제방송교류재단',
      deptName: '데이터정보화팀',
      title: '과장',
      phone: '02-3475-5434',
      email: 'ming@arirang.com',
    }),
    mj: null,
    itn: null,
    lawyer: null,
    lawyerAssignedDate: null,
    channelIdMj: 'chan-mj',
    channelIdLaw: 'chan-law',
    stage: null,
    ...overrides,
  }
}

function memo(overrides: Partial<WorkMemo> = {}): WorkMemo {
  return {
    id: 1,
    orgId: 1,
    contactName: '송민지 과장',
    body: '채널 생성 일정 문의',
    createdAt: new Date('2026-07-01T10:30:00').getTime(),
    ...overrides,
  }
}

beforeEach(() => {
  mocks.getMemos.mockReset()
  mocks.createMemo.mockReset()
  mocks.deleteMemo.mockReset()
})

describe('WorkMemos', () => {
  it('등록된 메모를 담당자와 본문과 함께 보여준다', async () => {
    mocks.getMemos.mockResolvedValue([memo()])

    render(<WorkMemos org={org()} />)

    await waitFor(() => expect(screen.getByRole('listitem')).toBeTruthy())

    const item = within(screen.getByRole('listitem'))
    expect(item.getByText('송민지 과장')).toBeTruthy()
    expect(item.getByText('채널 생성 일정 문의')).toBeTruthy()
  })

  it('메모가 없으면 안내 문구를 보여준다', async () => {
    mocks.getMemos.mockResolvedValue([])

    render(<WorkMemos org={org()} />)

    await waitFor(() => {
      expect(
        screen.getByText('등록된 메모가 없습니다. 통화 후 첫 메모를 남겨보세요.'),
      ).toBeTruthy()
    })
  })

  it('담당자 선택에 기관 담당자와 직접 입력이 나오고, 직접 입력을 고르면 이름 입력칸이 나타난다', async () => {
    mocks.getMemos.mockResolvedValue([])

    render(<WorkMemos org={org()} />)

    await waitFor(() => expect(mocks.getMemos).toHaveBeenCalled())

    expect(screen.getByRole('option', { name: '송민지 과장' })).toBeTruthy()
    expect(screen.getByRole('option', { name: '직접 입력' })).toBeTruthy()
    expect(screen.queryByLabelText('담당자 이름')).toBeNull()

    fireEvent.change(screen.getByLabelText('담당자'), { target: { value: '__custom__' } })

    expect(screen.getByLabelText('담당자 이름')).toBeTruthy()
  })

  it('신청기관 담당자, 문정원 담당자, 아이티앤 담당자, 담당 변호사가 모두 있으면 넷 다 선택 옵션으로 나온다', async () => {
    mocks.getMemos.mockResolvedValue([])

    render(
      <WorkMemos
        org={org({
          mj: contact({ id: 2, category: 'MJ', name: '김문정' }),
          itn: contact({ id: 4, category: 'ITN', name: '박아이티' }),
          lawyer: contact({ id: 3, category: 'LAWYER', name: '이변호' }),
        })}
      />,
    )

    await waitFor(() => expect(mocks.getMemos).toHaveBeenCalled())

    expect(screen.getByRole('option', { name: '송민지 과장' })).toBeTruthy()
    expect(screen.getByRole('option', { name: '김문정' })).toBeTruthy()
    expect(screen.getByRole('option', { name: '박아이티' })).toBeTruthy()
    expect(screen.getByRole('option', { name: '이변호' })).toBeTruthy()
    expect(screen.getByRole('option', { name: '직접 입력' })).toBeTruthy()
  })

  it('메모 내용이 비어 있으면 등록 버튼이 비활성이고, 입력하면 활성화된다', async () => {
    mocks.getMemos.mockResolvedValue([])

    render(<WorkMemos org={org()} />)

    await waitFor(() => expect(mocks.getMemos).toHaveBeenCalled())

    const submit = screen.getByRole('button', { name: '등록' })
    expect(submit).toBeDisabled()

    fireEvent.change(screen.getByLabelText('메모 내용'), { target: { value: '통화 내용' } })

    expect(submit).not.toBeDisabled()
  })

  it('등록을 누르면 선택된 담당자와 본문으로 createMemo를 호출하고 본문을 비운다', async () => {
    mocks.getMemos.mockResolvedValue([])
    mocks.createMemo.mockResolvedValue(memo({ id: 2, body: '통화 내용' }))

    render(<WorkMemos org={org()} />)

    fireEvent.change(screen.getByLabelText('담당자'), { target: { value: '송민지 과장' } })

    await waitFor(() => expect(mocks.getMemos).toHaveBeenCalled())

    fireEvent.change(screen.getByLabelText('메모 내용'), { target: { value: '통화 내용' } })
    fireEvent.click(screen.getByRole('button', { name: '등록' }))

    await waitFor(() => {
      expect(mocks.createMemo).toHaveBeenCalledWith(1, {
        contactName: '송민지 과장',
        body: '통화 내용',
      })
    })

    await waitFor(() => {
      expect((screen.getByLabelText('메모 내용') as HTMLTextAreaElement).value).toBe('')
    })
  })

  it('삭제를 누르면 확인을 물어보고, 확인하면 deleteMemo를 호출한다', async () => {
    mocks.getMemos.mockResolvedValue([memo()])
    mocks.deleteMemo.mockResolvedValue(undefined)
    const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true)

    render(<WorkMemos org={org()} />)

    await waitFor(() => expect(screen.getByText('채널 생성 일정 문의')).toBeTruthy())

    fireEvent.click(screen.getByRole('button', { name: '삭제' }))

    expect(confirmSpy).toHaveBeenCalledWith('이 메모를 삭제할까요?')
    await waitFor(() => expect(mocks.deleteMemo).toHaveBeenCalledWith(1, 1))

    confirmSpy.mockRestore()
  })

  it('삭제 확인을 취소하면 deleteMemo를 호출하지 않는다', async () => {
    mocks.getMemos.mockResolvedValue([memo()])
    const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false)

    render(<WorkMemos org={org()} />)

    await waitFor(() => expect(screen.getByText('채널 생성 일정 문의')).toBeTruthy())

    fireEvent.click(screen.getByRole('button', { name: '삭제' }))

    expect(confirmSpy).toHaveBeenCalled()
    expect(mocks.deleteMemo).not.toHaveBeenCalled()

    confirmSpy.mockRestore()
  })
})
