import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import { ChangeMyPasswordPage } from '../../../features/profile/pages/ChangeMyPasswordPage'
import { useAuthStore } from '../../../stores/authStore'

const API_URL = 'http://localhost:5000'

const mockNavigate = vi.fn()
vi.mock('react-router-dom', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-router-dom')>()
  return { ...actual, useNavigate: () => mockNavigate }
})

const authUser = {
  id: 1, username: 'admin', nombre: 'Admin', rol: 'admin',
  permisos: [], mustChangePassword: true,
}

const server = setupServer()

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => {
  server.resetHandlers()
  useAuthStore.getState().clearAuth()
  vi.clearAllMocks()
})
afterAll(() => server.close())

function renderPage() {
  useAuthStore.setState({ user: authUser })
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } })
  return render(
    <QueryClientProvider client={qc}>
      <MemoryRouter initialEntries={['/perfil/contrasena']}>
        <Routes>
          <Route path="/perfil/contrasena" element={<ChangeMyPasswordPage />} />
          <Route path="/" element={<div>Home</div>} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

// Helper: get form fields by their input id
function getOldPasswordInput() { return screen.getByLabelText('Contraseña actual') }
function getNewPasswordInput() { return screen.getByLabelText('Nueva contraseña') }
function getConfirmPasswordInput() { return screen.getByLabelText('Confirmar nueva contraseña') }
function getSubmitButton() { return screen.getByRole('button', { name: /cambiar contraseña/i }) }

describe('ChangeMyPasswordPage', () => {
  it('shows validation error when passwords do not match', async () => {
    server.use(
      http.put(`${API_URL}/api/v1/users/me/password`, () => {
        throw new Error('Should not be called')
      }),
    )

    renderPage()

    await userEvent.type(getOldPasswordInput(), 'current123')
    await userEvent.type(getNewPasswordInput(), 'NewPass123')
    await userEvent.type(getConfirmPasswordInput(), 'DifferentPass456')

    await userEvent.click(getSubmitButton())

    await waitFor(() =>
      expect(screen.getByText(/no coinciden/i)).toBeInTheDocument(),
    )
    expect(mockNavigate).not.toHaveBeenCalled()
  })

  it('no HTTP call when passwords do not match', async () => {
    let httpCalled = false
    server.use(
      http.put(`${API_URL}/api/v1/users/me/password`, () => {
        httpCalled = true
        return HttpResponse.json({}, { status: 204 })
      }),
    )

    renderPage()

    await userEvent.type(getOldPasswordInput(), 'current123')
    await userEvent.type(getNewPasswordInput(), 'NewPass123')
    await userEvent.type(getConfirmPasswordInput(), 'WrongConfirm')

    await userEvent.click(getSubmitButton())

    await new Promise((r) => setTimeout(r, 100))
    expect(httpCalled).toBe(false)
  })

  it('submit success → updates authStore mustChangePassword to false + navigate home', async () => {
    server.use(
      http.put(`${API_URL}/api/v1/users/me/password`, () =>
        new HttpResponse(null, { status: 204 }),
      ),
    )

    renderPage()

    await userEvent.type(getOldPasswordInput(), 'current123')
    await userEvent.type(getNewPasswordInput(), 'NewPass123')
    await userEvent.type(getConfirmPasswordInput(), 'NewPass123')

    await userEvent.click(getSubmitButton())

    await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/'))

    const state = useAuthStore.getState()
    expect(state.user?.mustChangePassword).toBe(false)
  })

  it('shows invalid-old-password error message on 400', async () => {
    server.use(
      http.put(`${API_URL}/api/v1/users/me/password`, () =>
        HttpResponse.json({ error: 'invalid-old-password' }, { status: 400 }),
      ),
    )

    renderPage()

    await userEvent.type(getOldPasswordInput(), 'wrongpassword')
    await userEvent.type(getNewPasswordInput(), 'NewPass123')
    await userEvent.type(getConfirmPasswordInput(), 'NewPass123')

    await userEvent.click(getSubmitButton())

    await waitFor(() =>
      expect(screen.getByText(/contraseña actual es incorrecta/i)).toBeInTheDocument(),
    )
    expect(mockNavigate).not.toHaveBeenCalled()
  })
})
