import { flushPromises, mount } from '@vue/test-utils'
import { afterEach, describe, expect, it, vi } from 'vitest'
import HomeScreen from './HomeScreen.vue'

// Экран судится по тому, что видит человек, а не по внутреннему состоянию:
// переписанная реализация обязана остаться зелёной.
//
// Переход к адресу входа проверяется тем, что его **нет**: своего входа у
// сервиса не осталось, и экран, снова начавший куда-то уводить, обязан упасть
// здесь, а не обнаружиться пустой страницей у человека.

const assign = vi.fn()

vi.stubGlobal('window', { location: { assign } })

function answerWith(response: Partial<Response> & { json?: () => Promise<unknown> }) {
  vi.stubGlobal('fetch', vi.fn().mockResolvedValue(response))
}

function failWith(error: Error) {
  vi.stubGlobal('fetch', vi.fn().mockRejectedValue(error))
}

afterEach(() => {
  vi.restoreAllMocks()
  assign.mockReset()
})

describe('экран показывает вошедшего', () => {
  it('показывает имя, когда сервис узнал пришедшего', async () => {
    answerWith({
      ok: true,
      status: 200,
      json: () => Promise.resolve({ id: 'u1', name: 'Антон' }),
    })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Антон')
    expect(assign).not.toHaveBeenCalled()
  })

  it('показывает текст сервера, когда тот не узнал пришедшего, и никуда не уводит', async () => {
    answerWith({
      ok: false,
      status: 401,
      json: () => Promise.resolve({ error_code: 'unauthorized', message: 'Сервис вас не узнал' }),
    })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Сервис вас не узнал')
    expect(assign).not.toHaveBeenCalled()
  })

  it('на неузнавание без текста сервера показывает общее, а не пустоту', async () => {
    answerWith({ ok: false, status: 401, json: () => Promise.resolve({}) })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Сервис не отвечает как надо')
  })

  it('на неразобранное тело показывает строку, а не падает', async () => {
    answerWith({
      ok: true,
      status: 200,
      json: () => Promise.reject(new SyntaxError('not json')),
    })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Сервис не отвечает как надо')
  })

  it('прочий отказ отличается от неузнавания', async () => {
    answerWith({
      ok: false,
      status: 500,
      json: () => Promise.resolve({ error_code: 'internal', message: 'Внутренняя ошибка сервиса' }),
    })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Внутренняя ошибка сервиса')
    expect(assign).not.toHaveBeenCalled()
  })

  it('на сорванный запрос показывает, что связи нет', async () => {
    failWith(new Error('network down'))

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Связи нет')
    expect(assign).not.toHaveBeenCalled()
  })

  it('учётная запись без имени: человек узнан, почты на экране нет', async () => {
    answerWith({
      ok: true,
      status: 200,
      json: () => Promise.resolve({ id: 'u1', name: '' }),
    })

    const screen = mount(HomeScreen)
    await flushPromises()

    expect(screen.text()).toContain('Вы вошли')
    expect(screen.text()).not.toContain('@')
  })
})
