import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createMemoryHistory, createRouter } from 'vue-router'
import BackendEventsListener from '../BackendEventsListener.vue'

// ── Hoisted mocks ────────────────────────────────────────────────────────────

const { mockLogoutUser, mockLogoutParent, mockSuppressForceLogout, mockCurrentUserId } = vi.hoisted(
  () => ({
    mockLogoutUser: vi.fn(),
    mockLogoutParent: vi.fn(),
    mockSuppressForceLogout: { value: false },
    mockCurrentUserId: { value: 'user-1' },
  }),
)

let capturedForceLogoutHandler: ((event: any) => void) | null = null

vi.mock('@/stores/auth', () => ({
  currentUserId: mockCurrentUserId,
  logoutParent: mockLogoutParent,
  logoutUser: mockLogoutUser,
  suppressForceLogout: mockSuppressForceLogout,
}))

vi.mock('@/common/backendEvents', () => ({
  useBackendEvents: vi.fn(),
}))

vi.mock('@/common/eventBus', () => ({
  eventBus: {
    on: vi.fn((event: string, handler: (e: any) => void) => {
      if (event === 'force_logout') capturedForceLogoutHandler = handler
    }),
    off: vi.fn(),
  },
}))

// ── Router ───────────────────────────────────────────────────────────────────

const mockRouter = createRouter({
  history: createMemoryHistory(),
  routes: [
    { path: '/', name: 'Landing', component: { template: '<div />' } },
    { path: '/auth/login', name: 'Login', component: { template: '<div />' } },
  ],
})

// ── Helpers ──────────────────────────────────────────────────────────────────

function mountListener() {
  return mount(BackendEventsListener, {
    global: { plugins: [mockRouter] },
  })
}

function fireForceLogout(reason?: string) {
  capturedForceLogoutHandler?.({ payload: reason ? { reason } : {} })
}

// ── Tests ────────────────────────────────────────────────────────────────────

describe('BackendEventsListener – force_logout', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    capturedForceLogoutHandler = null
    mockSuppressForceLogout.value = false
  })

  it('navigates to Login on force_logout with reason password_reset', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')

    fireForceLogout('password_reset')
    await flushPromises()

    expect(mockLogoutParent).toHaveBeenCalledOnce()
    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
  })

  it('navigates to / on force_logout with reason account_deleted', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')

    fireForceLogout('account_deleted')
    await flushPromises()

    expect(mockLogoutParent).toHaveBeenCalledOnce()
    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(pushSpy).toHaveBeenCalledWith('/')
  })

  it('navigates to Login on force_logout with unknown reason', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')

    fireForceLogout('something_else')
    await flushPromises()

    expect(mockLogoutParent).toHaveBeenCalledOnce()
    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
  })

  it('navigates to Login on force_logout with no reason', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')

    fireForceLogout()
    await flushPromises()

    expect(mockLogoutParent).toHaveBeenCalledOnce()
    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
  })

  it('skips logout and navigation when suppressForceLogout is true', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')
    mockSuppressForceLogout.value = true

    fireForceLogout('password_reset')
    await flushPromises()

    expect(mockLogoutParent).not.toHaveBeenCalled()
    expect(mockLogoutUser).not.toHaveBeenCalled()
    expect(pushSpy).not.toHaveBeenCalled()
  })

  it('clears suppressForceLogout after consuming a suppressed event', async () => {
    mountListener()
    mockSuppressForceLogout.value = true

    fireForceLogout('password_reset')
    await flushPromises()

    expect(mockSuppressForceLogout.value).toBe(false)
  })

  it('processes the next force_logout normally after suppression is consumed', async () => {
    mountListener()
    const pushSpy = vi.spyOn(mockRouter, 'push')
    mockSuppressForceLogout.value = true

    // First event: suppressed
    fireForceLogout('password_reset')
    await flushPromises()
    expect(mockLogoutUser).not.toHaveBeenCalled()

    // Second event: should go through normally
    fireForceLogout('password_reset')
    await flushPromises()
    expect(mockLogoutParent).toHaveBeenCalledOnce()
    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
  })
})
