import { describe, it, expect, beforeAll, beforeEach } from 'vitest'
import { getCustomerHistory } from '../../src/services/session.service.js'
import { SessionStatus } from '../../src/constants.js'
import { resetDb, resetAppConfig, db } from '../helpers/db.js'
import { createCustomer, createMitra } from '../helpers/fixtures.js'

// Phase 4 Stage 10 — Chat Tab Selesai feed uses cursor pagination.
// Cursor is base64url(`<endedAtIso>|<id>`); response is { items, next_cursor, has_more }.

describe('session.service.getCustomerHistory (cursor paginated)', () => {
  let customer
  let mitra

  beforeAll(async () => {
    await resetAppConfig()
  })

  beforeEach(async () => {
    await resetDb()
    customer = await createCustomer({ callName: 'Alice' })
    mitra = await createMitra({ callName: 'kak Sari' })
  })

  // Seed N completed sessions ended at deterministically spaced times.
  // i=0 is the OLDEST, i=N-1 is the NEWEST.
  const seedCompleted = async (count) => {
    const sql = db()
    const now = Date.now()
    const ids = []
    for (let i = 0; i < count; i++) {
      const endedAt = new Date(now - (count - i) * 60_000) // 1 min apart
      const [row] = await sql`
        INSERT INTO chat_sessions (
          customer_id, mitra_id, status, duration_minutes, price, ended_at, created_at
        )
        VALUES (
          ${customer.id}, ${mitra.id}, ${SessionStatus.COMPLETED}, 12, 5000,
          ${endedAt}, ${endedAt}
        )
        RETURNING id
      `
      ids.push(row.id)
    }
    return ids
  }

  it('returns empty + has_more=false when there is no history', async () => {
    const result = await getCustomerHistory(customer.id, { limit: 10 })
    expect(result.items).toEqual([])
    expect(result.has_more).toBe(false)
    expect(result.next_cursor).toBeNull()
  })

  it('returns all items + has_more=false when count <= limit', async () => {
    await seedCompleted(5)
    const result = await getCustomerHistory(customer.id, { limit: 10 })
    expect(result.items).toHaveLength(5)
    expect(result.has_more).toBe(false)
    expect(result.next_cursor).toBeNull()
  })

  it('first page returns `limit` items, has_more=true, and a usable cursor', async () => {
    await seedCompleted(7)
    const page1 = await getCustomerHistory(customer.id, { limit: 3 })
    expect(page1.items).toHaveLength(3)
    expect(page1.has_more).toBe(true)
    expect(page1.next_cursor).toBeTruthy()
  })

  it('walks across pages without duplicates or skips', async () => {
    const seeded = await seedCompleted(7) // ids[0]=oldest .. ids[6]=newest
    const collected = []

    let cursor = null
    do {
      const page = await getCustomerHistory(customer.id, { cursor, limit: 3 })
      collected.push(...page.items.map((r) => r.id))
      cursor = page.next_cursor
      if (!page.has_more) break
    } while (cursor)

    // Newest → oldest = reverse of seeded
    expect(collected).toEqual([...seeded].reverse())
    // No duplicates
    expect(new Set(collected).size).toBe(collected.length)
  })

  it('orders by ended_at DESC with id DESC tiebreak (no gaps for same-timestamp rows)', async () => {
    const sql = db()
    const sameTime = new Date()
    // Insert 3 rows with the EXACT same ended_at — only id distinguishes them.
    const ids = []
    for (let i = 0; i < 3; i++) {
      const [row] = await sql`
        INSERT INTO chat_sessions (
          customer_id, mitra_id, status, duration_minutes, price, ended_at, created_at
        )
        VALUES (
          ${customer.id}, ${mitra.id}, ${SessionStatus.COMPLETED}, 12, 5000,
          ${sameTime}, ${sameTime}
        )
        RETURNING id
      `
      ids.push(row.id)
    }

    // Page through them 1 at a time.
    const collected = []
    let cursor = null
    for (let i = 0; i < 5; i++) {
      const page = await getCustomerHistory(customer.id, { cursor, limit: 1 })
      collected.push(...page.items.map((r) => r.id))
      cursor = page.next_cursor
      if (!page.has_more) break
    }

    expect(collected).toHaveLength(3)
    expect(new Set(collected).size).toBe(3)
  })

  it('clamps limit to 50 max', async () => {
    await seedCompleted(60)
    const result = await getCustomerHistory(customer.id, { limit: 999 })
    expect(result.items.length).toBeLessThanOrEqual(50)
  })

  it('scopes by customer — does not leak other customers history', async () => {
    const other = await createCustomer({ callName: 'Bob' })
    const sql = db()
    await sql`
      INSERT INTO chat_sessions (customer_id, mitra_id, status, duration_minutes, price, ended_at)
      VALUES (${customer.id}, ${mitra.id}, ${SessionStatus.COMPLETED}, 12, 5000, NOW())
    `
    await sql`
      INSERT INTO chat_sessions (customer_id, mitra_id, status, duration_minutes, price, ended_at)
      VALUES (${other.id}, ${mitra.id}, ${SessionStatus.COMPLETED}, 12, 5000, NOW())
    `
    const result = await getCustomerHistory(customer.id, { limit: 20 })
    expect(result.items).toHaveLength(1)
  })

  it('includes CLOSING status (grace period) alongside COMPLETED', async () => {
    const sql = db()
    await sql`
      INSERT INTO chat_sessions (customer_id, mitra_id, status, duration_minutes, price, ended_at)
      VALUES (${customer.id}, ${mitra.id}, ${SessionStatus.CLOSING}, 12, 5000, NOW())
    `
    await sql`
      INSERT INTO chat_sessions (customer_id, mitra_id, status, duration_minutes, price, ended_at)
      VALUES (${customer.id}, ${mitra.id}, ${SessionStatus.COMPLETED}, 12, 5000, NOW())
    `
    const result = await getCustomerHistory(customer.id, { limit: 20 })
    expect(result.items).toHaveLength(2)
  })

  it('excludes ACTIVE / PENDING_PAYMENT / EXTENDING', async () => {
    const sql = db()
    for (const status of [SessionStatus.ACTIVE, SessionStatus.PENDING_PAYMENT, SessionStatus.EXTENDING]) {
      await sql`
        INSERT INTO chat_sessions (customer_id, mitra_id, status, duration_minutes, price)
        VALUES (${customer.id}, ${mitra.id}, ${status}, 12, 5000)
      `
    }
    const result = await getCustomerHistory(customer.id, { limit: 20 })
    expect(result.items).toHaveLength(0)
  })
})
