import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
import VerificationForm from '@/components/VerificationForm.vue'
import { useBatchStore } from '@/stores/batches'
import { useAuthStore } from '@/stores/auth'
import type { BatchDetailResponse } from '@/types'
import {
  emptyDraft,
  fieldRequirementsForBatchType,
} from '@/__tests__/helpers/fieldRequirements'

vi.mock('@/api/client', () => ({
  get: vi.fn(),
  post: vi.fn(),
  put: vi.fn(),
  del: vi.fn(),
  patch: vi.fn(),
  uploadFile: vi.fn(),
}))

vi.mock('@/composables/useToast', () => ({
  useToast: () => ({
    success: vi.fn(),
    error: vi.fn(),
    warning: vi.fn(),
    info: vi.fn(),
  }),
}))

vi.mock('vue-router', () => ({
  useRouter: () => ({
    push: vi.fn(),
  }),
  useRoute: () => ({
    params: {},
    query: {},
  }),
  createRouter: () => ({
    beforeEach: vi.fn(),
    afterEach: vi.fn(),
    push: vi.fn(),
    replace: vi.fn(),
  }),
  createWebHistory: () => ({}),
}))

vi.mock('@/router', () => ({
  default: {
    push: vi.fn(),
    replace: vi.fn(),
    beforeEach: vi.fn(),
  },
}))

const mountOptions = {
  global: {
    stubs: {
      // Render dialog content in-tree (ConfirmDialog uses Teleport)
      Teleport: { template: '<div><slot /></div>' },
    },
  },
}

function mountForm(
  props: { batch: BatchDetailResponse | null; batchId: string } = {
    batch: makeBatch(),
    batchId: 'b1',
  },
) {
  return mount(VerificationForm, { props, ...mountOptions })
}
function makeBatch(overrides: Partial<BatchDetailResponse> = {}): BatchDetailResponse {
  const batchType = overrides.batchType ?? 'VITALS'
  return {
    id: 'b1',
    status: 'PENDING_VERIFICATION',
    batchType,
    track: 'TRACK_A',
    fieldRequirements: fieldRequirementsForBatchType(batchType),
    patientId: 'p1',
    documentRef: 'docs/scan.pdf',
    documentUrl: null,
    enableRetroactiveAlerts: false,
    enteredByUserId: 'u1',
    verifiedByUserId: null,
    approvedByUserId: null,
    rejectionReason: null,
    promotedAt: null,
    promotionEncounterId: null,
    supersedesBatchId: null,
    clinicianAttestation: false,
    isCorrection: false,
    supersession: null,
    createdAt: '2026-06-27T10:00:00Z',
    updatedAt: '2026-06-27T10:00:00Z',
    ...overrides,
  }
}

function mountWithDraft(batchOverrides: Partial<BatchDetailResponse> = {}) {
  const batch = makeBatch(batchOverrides)
  const wrapper = mountForm({ batch, batchId: 'b1' })

  const store = useBatchStore()
  store.currentDraft = emptyDraft(batch.batchType, {
    patient: {
      id: 'dp1',
      fullName: 'Jane Doe',
      dateOfBirth: '1990-05-15',
      sex: 'female',
      bloodType: 'A+',
      emergencyContact: '555-1234',
      allergies: null,
      noKnownAllergies: false,
      medications: null,
      noActiveMedications: false,
    },
    encounter: {
      id: 'de1',
      batchId: 'b1',
      admissionDate: '2026-06-20T08:00:00',
      department: 'ICU',
      roomBed: '3A-12',
      admissionReason: 'Chest pain',
      dischargeDiagnosis: null,
      status: null,
    },
    observations: [
      {
        id: 'obs-1',
        batchId: 'b1',
        observationCode: 'HEART_RATE',
        value: 72,
        unit: 'bpm',
        recordedAt: '2026-06-27T10:00:00Z',
        note: null,
      },
      {
        id: 'obs-2',
        batchId: 'b1',
        observationCode: 'TEMP_C',
        value: 37.2,
        unit: 'C',
        recordedAt: '2026-06-27T10:00:00Z',
        note: null,
      },
    ],
  })

  return { wrapper, store }
}

beforeEach(() => {
  setActivePinia(createPinia())
  vi.clearAllMocks()
})

describe('VerificationForm', () => {
  it('renders verification header', () => {
    const wrapper = mountForm()
    expect(wrapper.text()).toContain('Verification Review')
    expect(wrapper.text()).toContain('Pending Verification')
  })

  it('renders patient fields with checkboxes after draft loads', async () => {
    const { wrapper } = mountWithDraft()
    await wrapper.vm.$nextTick()

    expect(wrapper.text()).toContain('Patient Demographics')
    expect(wrapper.text()).toContain('Jane Doe')
    expect(wrapper.text()).toContain('1990-05-15')

    const checkboxes = wrapper.findAll('input[type="checkbox"]')
    expect(checkboxes.length).toBeGreaterThan(0)
  })

  it('renders encounter fields after draft loads', async () => {
    const { wrapper } = mountWithDraft()
    await wrapper.vm.$nextTick()

    expect(wrapper.text()).toContain('Encounter Context')
    expect(wrapper.text()).toContain('ICU')
    expect(wrapper.text()).toContain('3A-12')
    expect(wrapper.text()).toContain('Chest pain')
  })

  it('shows rejection reason banner when present', () => {
    const wrapper = mountForm({ batch: makeBatch({ rejectionReason: 'Temperature seems incorrect' }), batchId: 'b1' })
    expect(wrapper.text()).toContain('Previous Rejection Reason')
    expect(wrapper.text()).toContain('Temperature seems incorrect')
  })

  it('does not show rejection banner when no reason', () => {
    const wrapper = mountForm({ batch: makeBatch({ rejectionReason: null }), batchId: 'b1' })
    expect(wrapper.text()).not.toContain('Previous Rejection Reason')
  })

  describe('field check progress', () => {
    it('shows 0/N checked initially', async () => {
      const { wrapper } = mountWithDraft()
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Fields verified:')
      expect(wrapper.text()).toMatch(/0\s*\/\s*\d+/)
    })

    it('updates count when checkboxes are toggled', async () => {
      const { wrapper } = mountWithDraft()
      await wrapper.vm.$nextTick()

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      await checkboxes[0].setValue(true)
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toMatch(/1\s*\/\s*\d+/)
    })
  })

  describe('approve button', () => {
    it('is disabled when not all fields are checked', async () => {
      const { wrapper } = mountWithDraft()
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper.findAll('button').find((b) => b.text().includes('Approve'))
      expect(approveBtn!.element.disabled).toBe(true)
    })

    it('is enabled when all fields are checked', async () => {
      const { wrapper } = mountWithDraft()
      await wrapper.vm.$nextTick()

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      for (const cb of checkboxes) {
        await cb.setValue(true)
      }
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper.findAll('button').find((b) => b.text().includes('Approve'))
      expect(approveBtn!.element.disabled).toBe(false)
    })

    it('calls verifyBatch with all field checks on approve', async () => {
      const { wrapper, store } = mountWithDraft()
      await wrapper.vm.$nextTick()

      store.verifyBatch = vi.fn().mockResolvedValue(undefined)

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      for (const cb of checkboxes) {
        await cb.setValue(true)
      }
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper.findAll('button').find((b) => b.text().includes('Approve'))
      await approveBtn!.trigger('click')
      await flushPromises()

      expect(store.verifyBatch).toHaveBeenCalledWith(
        'b1',
        expect.arrayContaining([
          expect.objectContaining({ passed: true }),
        ]),
        true,
      )
    })
  })

  describe('reject flow', () => {
    it('shows reject dialog when reject button is clicked', async () => {
      const wrapper = mountForm()

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      await rejectBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Reject Batch')
      expect(wrapper.text()).toContain('Confirm Rejection')
    })

    it('disables confirm button when reason is empty', async () => {
      const wrapper = mountForm()

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      await rejectBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      const confirmBtn = wrapper.findAll('button').find((b) => b.text() === 'Confirm Rejection')
      expect(confirmBtn!.element.disabled).toBe(true)
    })

    it('enables confirm button when reason is entered', async () => {
      const wrapper = mountForm()

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      await rejectBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      const textarea = wrapper.find('textarea')
      await textarea.setValue('Temperature value appears incorrect')
      await wrapper.vm.$nextTick()

      const confirmBtn = wrapper.findAll('button').find((b) => b.text() === 'Confirm Rejection')
      expect(confirmBtn!.element.disabled).toBe(false)
    })

    it('calls rejectBatch on confirm', async () => {
      const wrapper = mountForm()

      const store = useBatchStore()
      store.rejectBatch = vi.fn().mockResolvedValue(undefined)

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      await rejectBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      const textarea = wrapper.find('textarea')
      await textarea.setValue('Value incorrect')
      await wrapper.vm.$nextTick()

      const confirmBtn = wrapper.findAll('button').find((b) => b.text() === 'Confirm Rejection')
      await confirmBtn!.trigger('click')
      await flushPromises()

      expect(store.rejectBatch).toHaveBeenCalledWith('b1', 'Value incorrect')
    })

    it('closes reject dialog on cancel', async () => {
      const wrapper = mountForm()

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      await rejectBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Reject Batch')

      const cancelBtn = wrapper.findAll('button').find((b) => b.text() === 'Cancel')
      await cancelBtn!.trigger('click')
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).not.toContain('Reject Batch')
    })
  })

  describe('separation of duties', () => {
    it('shows SoD enforced banner and keeps Pass enabled for a different verifier', async () => {
      const auth = useAuthStore()
      auth.user = {
        id: 'verifier-2',
        username: 'verifier',
        fullName: 'Priya Nair',
        role: 'VERIFIER',
      }

      const { wrapper } = mountWithDraft({ enteredByUserId: 'entry-1' })
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Separation of Duties Enforced')
      expect(wrapper.text()).toContain('Priya Nair')

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      for (const cb of checkboxes) {
        await cb.setValue(true)
      }
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper
        .findAll('button')
        .find((b) => b.text().includes('Approve - Verified'))
      expect(approveBtn!.element.disabled).toBe(false)
    })

    it('blocks Pass when the current user entered the batch', async () => {
      const auth = useAuthStore()
      auth.user = {
        id: 'entry-1',
        username: 'clerk',
        fullName: 'Alex Clerk',
        role: 'VERIFIER',
      }

      const { wrapper } = mountWithDraft({ enteredByUserId: 'entry-1' })
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('You cannot verify a batch you entered.')

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      for (const cb of checkboxes) {
        await cb.setValue(true)
      }
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper
        .findAll('button')
        .find((b) => b.text().includes('Approve - Verified'))
      expect(approveBtn!.element.disabled).toBe(true)

      const rejectBtn = wrapper.findAll('button').find((b) => b.text() === 'Reject')
      expect(rejectBtn!.element.disabled).toBe(true)
    })
  })

  describe('observations display', () => {
    it('shows observation count in legend', async () => {
      const { wrapper } = mountWithDraft()
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Observations (2)')
    })
  })

  describe('allergy and medication verification', () => {
    it('shows allergy fields for ALLERGY_UPDATE batch', async () => {
      const { wrapper } = mountWithDraft({ batchType: 'ALLERGY_UPDATE' })
      const store = useBatchStore()
      store.currentDraft!.patient!.allergies = ['Penicillin', 'Latex']
      await wrapper.vm.$nextTick()

      // Re-trigger the watcher by resetting draft
      const draft = { ...store.currentDraft! }
      store.currentDraft = null
      await wrapper.vm.$nextTick()
      store.currentDraft = draft
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('Allergies')
    })

    it('shows NKA for noKnownAllergies', async () => {
      const wrapper = mountForm({
        batch: makeBatch({ batchType: 'ALLERGY_UPDATE' }),
        batchId: 'b1',
      })

      const store = useBatchStore()
      store.currentDraft = emptyDraft('ALLERGY_UPDATE', {
        patient: {
          id: 'dp1',
          fullName: 'Jane',
          dateOfBirth: '1990-01-01',
          sex: 'female',
          bloodType: null,
          emergencyContact: null,
          allergies: null,
          noKnownAllergies: true,
          medications: null,
          noActiveMedications: false,
        },
        encounter: {
          id: 'de1',
          batchId: 'b1',
          admissionDate: null,
          department: null,
          roomBed: null,
          admissionReason: null,
          dischargeDiagnosis: null,
          status: null,
        },
        observations: [],
      })
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).toContain('No Known Allergies')
      expect(wrapper.text()).toContain('Yes (NKA)')
    })
  })

  describe('error handling', () => {
    it('shows error message on approve failure', async () => {
      const { wrapper, store } = mountWithDraft()
      await wrapper.vm.$nextTick()

      store.verifyBatch = vi.fn().mockRejectedValue(new Error('Server error'))

      const checkboxes = wrapper.findAll('input[type="checkbox"]')
      for (const cb of checkboxes) {
        await cb.setValue(true)
      }
      await wrapper.vm.$nextTick()

      const approveBtn = wrapper.findAll('button').find((b) => b.text().includes('Approve'))
      await approveBtn!.trigger('click')
      await flushPromises()

      expect(wrapper.text()).toContain('Server error')
    })
  })
})
