import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'

// Keep external sockets / FCM no-op so buildPublic doesn't try to open them.
vi.mock('../../src/plugins/websocket.js', () => ({
  sendToUser: vi.fn(() => false),
  sendToSessionParticipant: vi.fn(() => false),
  registerWebSocketPlugin: vi.fn(async () => {}),
  registerWebSocketRoute: vi.fn(),
  isUserOnlineWs: vi.fn(() => false),
  getSessionConnections: vi.fn(() => ({})),
}))

vi.mock('../../src/services/notification.service.js', () => ({
  sendPushNotification: vi.fn(async () => true),
  registerDeviceToken: vi.fn(async () => {}),
}))

const { buildPublic } = await import('../helpers/server.js')
const { createCustomer } = await import('../helpers/fixtures.js')
const { resetDbHard, db } = await import('../helpers/db.js')
const { customerJwt, authHeader } = await import('../helpers/jwt.js')
const {
  getCustomerById,
  markCustomerUspSeen,
} = await import('../../src/services/customer.service.js')

describe('Phase 4 — USP one-time gate', () => {
  let app

  beforeAll(async () => {
    app = await buildPublic()
  })

  afterAll(async () => {
    await app.close()
  })

  beforeEach(async () => {
    await resetDbHard()
  })

  describe('migration default', () => {
    it('new customer row has usp_seen = false', async () => {
      const c = await createCustomer({ callName: 'New User' })
      const row = await getCustomerById(c.id)
      expect(row).toBeTruthy()
      expect(row.usp_seen).toBe(false)
    })
  })

  describe('markCustomerUspSeen() service', () => {
    it('flips false → true and returns the updated row', async () => {
      const c = await createCustomer({ callName: 'Marker' })
      const updated = await markCustomerUspSeen(c.id)
      expect(updated.usp_seen).toBe(true)
      const reread = await getCustomerById(c.id)
      expect(reread.usp_seen).toBe(true)
    })

    it('is idempotent — second call still returns usp_seen=true, no error', async () => {
      const c = await createCustomer({ callName: 'Idem' })
      await markCustomerUspSeen(c.id)
      const second = await markCustomerUspSeen(c.id)
      expect(second.usp_seen).toBe(true)
    })
  })

  describe('POST /api/client/auth/usp-seen', () => {
    it('returns 401 when no Authorization header is present', async () => {
      const res = await app.inject({
        method: 'POST',
        url: '/api/client/auth/usp-seen',
      })
      expect(res.statusCode).toBe(401)
    })

    it('returns 200 + flips flag for an authed customer', async () => {
      const c = await createCustomer({ callName: 'Authed' })
      const res = await app.inject({
        method: 'POST',
        url: '/api/client/auth/usp-seen',
        headers: authHeader(customerJwt(c.id)),
      })
      expect(res.statusCode).toBe(200)
      const body = res.json()
      expect(body.success).toBe(true)
      expect(body.data.id).toBe(c.id)
      expect(body.data.usp_seen).toBe(true)

      // DB persisted
      const reread = await getCustomerById(c.id)
      expect(reread.usp_seen).toBe(true)
    })

    it('rejects a non-customer JWT (mitra) with 403', async () => {
      // Mint a JWT that says CUSTOMER but the route still asserts type — the
      // route reads user_type from the JWT claim, so use mitraJwt for negative.
      const { mitraJwt } = await import('../helpers/jwt.js')
      const fakeId = '00000000-0000-0000-0000-000000000001'
      const res = await app.inject({
        method: 'POST',
        url: '/api/client/auth/usp-seen',
        headers: authHeader(mitraJwt(fakeId)),
      })
      expect(res.statusCode).toBe(403)
    })
  })

  describe('GET /api/client/auth/me payload', () => {
    it('includes usp_seen in the response (false for fresh customer)', async () => {
      const c = await createCustomer({ callName: 'Reader' })
      const res = await app.inject({
        method: 'GET',
        url: '/api/client/auth/me',
        headers: authHeader(customerJwt(c.id)),
      })
      expect(res.statusCode).toBe(200)
      const body = res.json()
      expect(body.data).toHaveProperty('usp_seen')
      expect(body.data.usp_seen).toBe(false)
    })

    it('reflects usp_seen=true after the flag has been set', async () => {
      const c = await createCustomer({ callName: 'Reader2' })
      await markCustomerUspSeen(c.id)
      const res = await app.inject({
        method: 'GET',
        url: '/api/client/auth/me',
        headers: authHeader(customerJwt(c.id)),
      })
      expect(res.statusCode).toBe(200)
      expect(res.json().data.usp_seen).toBe(true)
    })
  })
})
