// @vitest-environment node
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createError } from 'h3'

const sendMail = vi.fn().mockResolvedValue({ messageId: 'test' })
const createTransport = vi.fn((_options?: unknown) => ({ sendMail }))

vi.mock('nodemailer', () => ({
  default: { createTransport: (options: unknown) => createTransport(options) },
}))

const mockConfig = {
  smtpHost: 'smtp.test.local',
  smtpPort: 587,
  smtpSecure: 'true',
  smtpUser: 'smtp-user',
  smtpPass: 'smtp-pass',
  smtpAllowInsecureTls: 'false',
  contactFromEmail: 'from@dleag.org',
  contactBccEmail: '',
}

let requestBody: unknown

vi.stubGlobal('defineEventHandler', (fn: (event: unknown) => unknown) => fn)
vi.stubGlobal('readBody', vi.fn(() => Promise.resolve(requestBody)))
vi.stubGlobal('useRuntimeConfig', vi.fn(() => mockConfig))
vi.stubGlobal('createError', createError)

const contactModule = await import('./contact.post')
const handler = contactModule.default as unknown as (event: object) => Promise<{ ok: boolean }>

beforeEach(() => {
  sendMail.mockClear()
  createTransport.mockClear()
})

describe('POST /api/contact', () => {
  it('rejects a request missing required fields with a 400', async () => {
    requestBody = { name: '', email: '', subject: '', message: '' }
    await expect(handler({})).rejects.toMatchObject({ statusCode: 400 })
    expect(sendMail).not.toHaveBeenCalled()
  })

  it('rejects a request missing only the message', async () => {
    requestBody = { name: 'Jane', email: 'jane@example.com', subject: 'programs' }
    await expect(handler({})).rejects.toMatchObject({ statusCode: 400 })
  })

  it('sends an email and returns ok for a valid submission', async () => {
    requestBody = { name: 'Jane Doe', email: 'jane@example.com', subject: 'programs', message: 'Hi there' }
    const result = await handler({})

    expect(result).toEqual({ ok: true })
    expect(createTransport).toHaveBeenCalledWith(expect.objectContaining({
      host: 'smtp.test.local',
      port: 587,
      secure: true,
    }))
    expect(sendMail).toHaveBeenCalledWith(expect.objectContaining({
      to: 'jane@example.com',
      replyTo: 'from@dleag.org',
      subject: expect.stringContaining('Thanks for contacting'),
    }))
  })

  it('escapes HTML in the submitted fields to prevent injection', async () => {
    requestBody = {
      name: '<script>alert(1)</script>',
      email: 'x@x.com',
      subject: 'other',
      message: 'line one\nline two',
    }
    await handler({})

    const html = sendMail.mock.calls[0][0].html as string
    expect(html).toContain('&lt;script&gt;alert(1)&lt;/script&gt;')
    expect(html).not.toContain('<script>alert(1)</script>')
    expect(html).toContain('line one<br>line two')
  })

  it('falls back to "N/A" when no phone number is provided', async () => {
    requestBody = { name: 'Jane', email: 'jane@example.com', subject: 'volunteer', message: 'hi' }
    await handler({})

    const html = sendMail.mock.calls[0][0].html as string
    expect(html).toContain('N/A')
  })

  it('combines country code and phone when a phone number is provided', async () => {
    requestBody = { name: 'Jane', email: 'jane@example.com', countryCode: '+63', phone: '9171234567', subject: 'volunteer', message: 'hi' }
    await handler({})

    const html = sendMail.mock.calls[0][0].html as string
    expect(html).toContain('+63 9171234567')
  })

  it('maps a known subject value to its readable label', async () => {
    requestBody = { name: 'Jane', email: 'jane@example.com', subject: 'partner', message: 'hi' }
    await handler({})

    const html = sendMail.mock.calls[0][0].html as string
    expect(html).toContain('Partnership')
  })

  it('falls back to the raw subject value when it is unrecognized', async () => {
    requestBody = { name: 'Jane', email: 'jane@example.com', subject: 'unknown-subject', message: 'hi' }
    await handler({})

    const html = sendMail.mock.calls[0][0].html as string
    expect(html).toContain('unknown-subject')
  })
})
