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

// ── Mocks ────────────────────────────────────────────────────────────────────

global.fetch = vi.fn()

const { mockLogoutUser } = vi.hoisted(() => ({ mockLogoutUser: vi.fn() }))
vi.mock('@/stores/auth', () => ({ logoutUser: mockLogoutUser }))
vi.mock('@/common/api', () => ({
  isPasswordStrong: (p: string) => p.length >= 8 && /[a-zA-Z]/.test(p) && /[0-9]/.test(p),
}))

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

function mountWithToken(token = 'valid-token') {
  return mount(ResetPassword, {
    global: {
      plugins: [mockRouter],
      stubs: { ModalDialog: { template: '<div class="modal"><slot /></div>' } },
    },
  })
}

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

describe('ResetPassword', () => {
  beforeEach(async () => {
    vi.clearAllMocks()
    ;(global.fetch as any).mockClear()
    // navigate to the route with a token query param
    await mockRouter.push('/auth/reset-password?token=valid-token')
  })

  it('validates the token on mount and shows the form when valid', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true })
    const wrapper = mountWithToken()
    await flushPromises()

    expect(wrapper.vm.tokenValid).toBe(true)
    expect(wrapper.vm.tokenChecked).toBe(true)
  })

  it('shows error and redirects when token is invalid', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({
      ok: false,
      json: async () => ({ error: 'Token expired' }),
    })
    const pushSpy = vi.spyOn(mockRouter, 'push')
    mountWithToken()
    await flushPromises()

    expect(pushSpy).toHaveBeenCalledWith({ name: 'AuthLanding' })
  })

  it('shows error and redirects on network failure during token check', async () => {
    ;(global.fetch as any).mockRejectedValueOnce(new Error('Network'))
    const pushSpy = vi.spyOn(mockRouter, 'push')
    mountWithToken()
    await flushPromises()

    expect(pushSpy).toHaveBeenCalledWith({ name: 'AuthLanding' })
  })

  it('submit button is disabled when passwords are weak or mismatched', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true })
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'weak'
    wrapper.vm.confirmPassword = 'weak'
    await nextTick()

    expect(wrapper.vm.formValid).toBeFalsy()
  })

  it('submit button is enabled when passwords are strong and match', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true })
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'StrongPass1'
    wrapper.vm.confirmPassword = 'StrongPass1'
    await nextTick()

    expect(wrapper.vm.formValid).toBeTruthy()
  })

  it('calls /api/auth/reset-password with token and new password on submit', async () => {
    ;(global.fetch as any)
      .mockResolvedValueOnce({ ok: true }) // token validation
      .mockResolvedValueOnce({ ok: true }) // reset call
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'NewPass123'
    wrapper.vm.confirmPassword = 'NewPass123'
    await wrapper.vm.submitForm()

    expect(global.fetch).toHaveBeenLastCalledWith(
      '/api/auth/reset-password',
      expect.objectContaining({
        method: 'POST',
        body: JSON.stringify({ token: 'valid-token', password: 'NewPass123' }),
      }),
    )
  })

  it('shows success modal and calls logoutUser after successful reset', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true }).mockResolvedValueOnce({ ok: true })
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'NewPass123'
    wrapper.vm.confirmPassword = 'NewPass123'
    await wrapper.vm.submitForm()
    await nextTick()

    expect(mockLogoutUser).toHaveBeenCalledOnce()
    expect(wrapper.vm.showModal).toBe(true)
  })

  it('shows error message on failed reset', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true }).mockResolvedValueOnce({
      ok: false,
      json: async () => ({ error: 'Token expired' }),
      text: async () => '',
    })
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'NewPass123'
    wrapper.vm.confirmPassword = 'NewPass123'
    await wrapper.vm.submitForm()
    await nextTick()

    expect(wrapper.vm.errorMsg).toBe('Token expired')
    expect(wrapper.vm.showModal).toBe(false)
  })

  it('clears password fields after successful reset', async () => {
    ;(global.fetch as any).mockResolvedValueOnce({ ok: true }).mockResolvedValueOnce({ ok: true })
    const wrapper = mountWithToken()
    await flushPromises()

    wrapper.vm.password = 'NewPass123'
    wrapper.vm.confirmPassword = 'NewPass123'
    await wrapper.vm.submitForm()
    await nextTick()

    expect(wrapper.vm.password).toBe('')
    expect(wrapper.vm.confirmPassword).toBe('')
  })
})
