﻿import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import SystemPage from './SystemPage'
import { withCodes } from '../codes/fixtures'
import type { ResetTarget } from '../api/client'

const mocks = vi.hoisted(() => ({
  getResetPreview: vi.fn(),
  resetChannels: vi.fn(),
}))

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

function target(overrides: Partial<ResetTarget> = {}): ResetTarget {
  return {
    orgId: 1,
    orgNo: '001',
    orgName: '국제방송교류재단',
    stage: 1,
    channels: [
      { kind: '문정원', channelId: 'ch-mj' },
      { kind: '법률검토', channelId: 'ch-law' },
    ],
    ...overrides,
  }
}

async function typeConfirmAndRun(phrase = '채널 초기화') {
  fireEvent.change(screen.getByLabelText(/실행하려면/), { target: { value: phrase } })
  fireEvent.click(screen.getByText('채널 초기화 실행'))
  fireEvent.click(await screen.findByText('초기화 실행'))
}

describe('SystemPage', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mocks.getResetPreview.mockResolvedValue([target()])
    mocks.resetChannels.mockResolvedValue({ archivedChannels: 2, clearedOrgs: 1, failures: [] })
  })

  it('초기화 대상을 기관·채널 수와 함께 미리 보여준다', async () => {
    mocks.getResetPreview.mockResolvedValue([
      target(),
      target({ orgId: 2, orgNo: '002', orgName: '세종학당재단', channels: [{ kind: '문정원', channelId: 'c2' }] }),
    ])
    render(withCodes(<SystemPage />))

    expect(await screen.findByText('대상 2개 기관 · 채널 3개')).toBeTruthy()
    const rows = within(screen.getByTestId('reset-targets')).getAllByRole('row')
    expect(rows).toHaveLength(3) // 헤더 + 2
    expect(rows[1].textContent).toContain('문정원 · 법률검토')
  })

  it('확인 문구를 입력하기 전에는 실행 버튼이 잠겨 있다', async () => {
    render(withCodes(<SystemPage />))
    await screen.findByTestId('reset-targets')

    expect(screen.getByText('채널 초기화 실행')).toHaveProperty('disabled', true)

    fireEvent.change(screen.getByLabelText(/실행하려면/), { target: { value: '초기화' } })
    expect(screen.getByText('채널 초기화 실행')).toHaveProperty('disabled', true)

    fireEvent.change(screen.getByLabelText(/실행하려면/), { target: { value: '채널 초기화' } })
    expect(screen.getByText('채널 초기화 실행')).toHaveProperty('disabled', false)
  })

  it('실행 버튼을 눌러도 확인창에서 한 번 더 물어본다', async () => {
    render(withCodes(<SystemPage />))
    await screen.findByTestId('reset-targets')

    fireEvent.change(screen.getByLabelText(/실행하려면/), { target: { value: '채널 초기화' } })
    fireEvent.click(screen.getByText('채널 초기화 실행'))

    expect(await screen.findByText('정말 초기화할까요?')).toBeTruthy()
    expect(mocks.resetChannels).not.toHaveBeenCalled()

    fireEvent.click(screen.getByText('취소'))
    expect(mocks.resetChannels).not.toHaveBeenCalled()
  })

  it('확인창에서 실행하면 문구와 함께 초기화를 요청하고 결과를 보여준다', async () => {
    render(withCodes(<SystemPage />))
    await screen.findByTestId('reset-targets')

    await typeConfirmAndRun()

    await waitFor(() => expect(mocks.resetChannels).toHaveBeenCalledWith('채널 초기화'))
    expect(await screen.findByText('채널 2개 보관, 기관 1곳 초기화')).toBeTruthy()
    // 실행 후 목록을 다시 읽는다
    expect(mocks.getResetPreview).toHaveBeenCalledTimes(2)
  })

  it('실패가 있으면 사유를 그대로 보여준다', async () => {
    mocks.resetChannels.mockResolvedValue({
      archivedChannels: 1,
      clearedOrgs: 0,
      failures: ['001 국제방송교류재단 (법률검토): 권한 없음'],
    })
    render(withCodes(<SystemPage />))
    await screen.findByTestId('reset-targets')

    await typeConfirmAndRun()

    expect(await screen.findByText(/실패 1건/)).toBeTruthy()
    expect(screen.getByText(/권한 없음/)).toBeTruthy()
  })

  it('대상이 없으면 실행할 수 없다', async () => {
    mocks.getResetPreview.mockResolvedValue([])
    render(withCodes(<SystemPage />))

    expect(await screen.findByText('초기화할 채널이 없습니다.')).toBeTruthy()
    fireEvent.change(screen.getByLabelText(/실행하려면/), { target: { value: '채널 초기화' } })
    expect(screen.getByText('채널 초기화 실행')).toHaveProperty('disabled', true)
  })

  it('무엇이 남고 무엇이 사라지는지 화면에 적어 둔다', async () => {
    render(withCodes(<SystemPage />))
    await screen.findByTestId('reset-targets')

    expect(screen.getByText(/보관\(아카이브\)/)).toBeTruthy()
    expect(screen.getByText(/그대로 남는다/)).toBeTruthy()
    expect(screen.getByText(/사용자 계정도 건드리지 않는다/)).toBeTruthy()
  })
})
