import { describe, it, expect } from 'vitest'
import {
  calculateTicketPrice,
  checkTicketAvailability,
  validateTicketPurchase,
  formatPrice,
  calculateSeriesTicketPrice,
  checkSeriesTicketAvailability,
  validateSeriesTicketPurchase,
  checkUserSeriesPass
} from '../../../server/utils/tickets.js'

// ---------------------------------------------------------------------------
// Helpers — build minimal event / series / member stubs
// ---------------------------------------------------------------------------

const futureDate = () => new Date(Date.now() + 86400000).toISOString()
const pastDate = () => new Date(Date.now() - 86400000).toISOString()

const baseMember = (overrides = {}) => ({
  email: 'member@example.com',
  circle: 'community',
  ...overrides
})

const legacyFreeEvent = (overrides = {}) => ({
  startDate: futureDate(),
  registrations: [],
  ...overrides
})

const legacyPaidEvent = (overrides = {}) => ({
  startDate: futureDate(),
  registrations: [],
  pricing: { paymentRequired: true, isFree: false, publicPrice: 25, currency: 'CAD' },
  ...overrides
})

const ticketedEvent = (overrides = {}) => ({
  startDate: futureDate(),
  registrations: [],
  tickets: {
    enabled: true,
    currency: 'CAD',
    capacity: { total: 100 },
    member: {
      available: true,
      price: 10,
      isFree: false,
      name: 'Member Ticket',
      description: 'For members'
    },
    public: {
      available: true,
      price: 30,
      quantity: 50,
      sold: 0,
      reserved: 0,
      name: 'General Admission',
      description: 'Public ticket'
    },
    waitlist: { enabled: false },
    ...overrides.tickets
  },
  ...overrides
})

const ticketedSeries = (overrides = {}) => ({
  isActive: true,
  registrations: [],
  tickets: {
    enabled: true,
    currency: 'CAD',
    capacity: { total: 50 },
    member: {
      available: true,
      price: 20,
      isFree: false,
      name: 'Member Series Pass',
      description: 'For members'
    },
    public: {
      available: true,
      price: 60,
      quantity: 30,
      sold: 0,
      reserved: 0,
      name: 'Series Pass',
      description: 'Public series pass'
    },
    waitlist: { enabled: false },
    ...overrides.tickets
  },
  ...overrides
})

// ===========================================================================
// calculateTicketPrice
// ===========================================================================

describe('calculateTicketPrice', () => {
  describe('legacy pricing (tickets not enabled)', () => {
    it('returns free guest ticket for events without payment requirement', () => {
      const event = legacyFreeEvent()
      const result = calculateTicketPrice(event)

      expect(result).toEqual({
        ticketType: 'guest',
        price: 0,
        currency: 'CAD',
        isEarlyBird: false,
        isFree: true
      })
    })

    it('returns free ticket for member on paid event', () => {
      const event = legacyPaidEvent()
      const member = baseMember()
      const result = calculateTicketPrice(event, member)

      expect(result.ticketType).toBe('member')
      expect(result.price).toBe(0)
      expect(result.isFree).toBe(true)
    })

    it('returns public price for non-member on paid event', () => {
      const event = legacyPaidEvent()
      const result = calculateTicketPrice(event, null)

      expect(result.ticketType).toBe('public')
      expect(result.price).toBe(25)
      expect(result.isFree).toBe(false)
    })

    it('returns guest/free when pricing.isFree is true even if paymentRequired', () => {
      const event = legacyFreeEvent({
        pricing: { paymentRequired: true, isFree: true, publicPrice: 10 }
      })
      const result = calculateTicketPrice(event)

      expect(result.ticketType).toBe('guest')
      expect(result.price).toBe(0)
      expect(result.isFree).toBe(true)
    })
  })

  describe('member ticket pricing', () => {
    it('returns base member price when isFree is false', () => {
      const event = ticketedEvent()
      const member = baseMember()
      const result = calculateTicketPrice(event, member)

      expect(result.ticketType).toBe('member')
      expect(result.price).toBe(10)
      expect(result.isFree).toBe(false)
      expect(result.name).toBe('Member Ticket')
      expect(result.description).toBe('For members')
    })

    it('returns price 0 when member ticket isFree is true', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: { available: true, price: 10, isFree: true },
          public: { available: true, price: 30 }
        }
      })
      const member = baseMember()
      const result = calculateTicketPrice(event, member)

      expect(result.price).toBe(0)
      expect(result.isFree).toBe(true)
    })

    it('applies circle override price', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: {
            available: true,
            price: 10,
            isFree: false,
            circleOverrides: {
              founder: { price: 5 }
            }
          },
          public: { available: true, price: 30 }
        }
      })
      const member = baseMember({ circle: 'founder' })
      const result = calculateTicketPrice(event, member)

      expect(result.price).toBe(5)
      expect(result.isFree).toBe(false)
    })

    it('applies circle override isFree', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: {
            available: true,
            price: 10,
            isFree: false,
            circleOverrides: {
              practitioner: { isFree: true }
            }
          },
          public: { available: true, price: 30 }
        }
      })
      const member = baseMember({ circle: 'practitioner' })
      const result = calculateTicketPrice(event, member)

      expect(result.price).toBe(0)
      expect(result.isFree).toBe(true)
    })

    it('ignores circle override for a different circle', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: {
            available: true,
            price: 10,
            isFree: false,
            circleOverrides: {
              practitioner: { isFree: true }
            }
          },
          public: { available: true, price: 30 }
        }
      })
      const member = baseMember({ circle: 'community' })
      const result = calculateTicketPrice(event, member)

      expect(result.price).toBe(10)
      expect(result.isFree).toBe(false)
    })
  })

  describe('public ticket pricing', () => {
    it('returns base public price when no member provided', () => {
      const event = ticketedEvent()
      const result = calculateTicketPrice(event, null)

      expect(result.ticketType).toBe('public')
      expect(result.price).toBe(30)
      expect(result.isEarlyBird).toBe(false)
      expect(result.isFree).toBe(false)
      expect(result.name).toBe('General Admission')
    })

    it('returns early bird price before deadline', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: { available: false },
          public: {
            available: true,
            price: 30,
            earlyBirdPrice: 20,
            earlyBirdDeadline: futureDate()
          }
        }
      })
      const result = calculateTicketPrice(event, null)

      expect(result.price).toBe(20)
      expect(result.isEarlyBird).toBe(true)
      expect(result.isFree).toBe(false)
    })

    it('returns regular price after early bird deadline', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: { available: false },
          public: {
            available: true,
            price: 30,
            earlyBirdPrice: 20,
            earlyBirdDeadline: pastDate()
          }
        }
      })
      const result = calculateTicketPrice(event, null)

      expect(result.price).toBe(30)
      expect(result.isEarlyBird).toBe(false)
    })

    it('returns isFree true when public price is 0', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: { available: false },
          public: { available: true, price: 0 }
        }
      })
      const result = calculateTicketPrice(event, null)

      expect(result.price).toBe(0)
      expect(result.isFree).toBe(true)
    })
  })

  describe('no tickets available', () => {
    it('returns null when member tickets unavailable and no public tickets', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          member: { available: false },
          public: { available: false }
        }
      })
      const result = calculateTicketPrice(event, null)

      expect(result).toBeNull()
    })

    it('returns null for non-member when only member tickets available', () => {
      const event = ticketedEvent({
        tickets: {
          enabled: true,
          currency: 'CAD',
          member: { available: true, price: 10, isFree: false },
          public: { available: false }
        }
      })
      const result = calculateTicketPrice(event, null)

      expect(result).toBeNull()
    })
  })
})

// ===========================================================================
// checkTicketAvailability
// ===========================================================================

describe('checkTicketAvailability', () => {
  describe('legacy (tickets not enabled)', () => {
    it('returns available with null remaining when no maxAttendees', () => {
      const event = legacyFreeEvent()
      const result = checkTicketAvailability(event)

      expect(result).toEqual({
        available: true,
        remaining: null,
        waitlistAvailable: false
      })
    })

    it('returns available with remaining count when under capacity', () => {
      const event = legacyFreeEvent({
        maxAttendees: 10,
        registrations: [{ email: 'a@b.com' }, { email: 'c@d.com' }]
      })
      const result = checkTicketAvailability(event)

      expect(result.available).toBe(true)
      expect(result.remaining).toBe(8)
    })

    it('returns unavailable at capacity', () => {
      const event = legacyFreeEvent({
        maxAttendees: 2,
        registrations: [{ email: 'a@b.com' }, { email: 'c@d.com' }]
      })
      const result = checkTicketAvailability(event)

      expect(result.available).toBe(false)
      expect(result.remaining).toBe(0)
    })
  })

  describe('ticketed events — overall capacity', () => {
    it('returns unavailable when total capacity exceeded', () => {
      const event = ticketedEvent()
      event.tickets.capacity.total = 2
      event.registrations = [{ email: 'a@b.com' }, { email: 'c@d.com' }]

      const result = checkTicketAvailability(event, 'public')

      expect(result.available).toBe(false)
      expect(result.remaining).toBe(0)
    })

    it('respects waitlist flag when capacity exceeded', () => {
      const event = ticketedEvent()
      event.tickets.capacity.total = 1
      event.tickets.waitlist = { enabled: true }
      event.registrations = [{ email: 'a@b.com' }]

      const result = checkTicketAvailability(event, 'public')

      expect(result.available).toBe(false)
      expect(result.waitlistAvailable).toBe(true)
    })
  })

  describe('ticketed events — public tickets', () => {
    it('returns correct remaining for quantity-limited public tickets', () => {
      const event = ticketedEvent()
      event.tickets.public.quantity = 10
      event.tickets.public.sold = 3
      event.tickets.public.reserved = 2

      const result = checkTicketAvailability(event, 'public')

      expect(result.available).toBe(true)
      expect(result.remaining).toBe(5)
    })

    it('returns unavailable when public tickets sold out', () => {
      const event = ticketedEvent()
      event.tickets.public.quantity = 5
      event.tickets.public.sold = 5
      event.tickets.public.reserved = 0

      const result = checkTicketAvailability(event, 'public')

      expect(result.available).toBe(false)
      expect(result.remaining).toBe(0)
    })

    it('returns unlimited when no quantity set on public tickets', () => {
      const event = ticketedEvent()
      delete event.tickets.public.quantity

      const result = checkTicketAvailability(event, 'public')

      expect(result.available).toBe(true)
      expect(result.remaining).toBeNull()
    })
  })

  describe('ticketed events — member tickets', () => {
    it('returns available with capacity remaining', () => {
      const event = ticketedEvent()
      event.tickets.capacity.total = 10
      event.registrations = [{ email: 'a@b.com' }]

      const result = checkTicketAvailability(event, 'member')

      expect(result.available).toBe(true)
      expect(result.remaining).toBe(9)
    })

    it('returns unlimited when no total capacity set', () => {
      const event = ticketedEvent()
      delete event.tickets.capacity

      const result = checkTicketAvailability(event, 'member')

      expect(result.available).toBe(true)
      expect(result.remaining).toBeNull()
    })
  })

  describe('edge cases', () => {
    it('returns unavailable for unknown ticket type', () => {
      const event = ticketedEvent()
      const result = checkTicketAvailability(event, 'vip')

      expect(result.available).toBe(false)
      expect(result.remaining).toBe(0)
    })
  })
})

// ===========================================================================
// validateTicketPurchase
// ===========================================================================

describe('validateTicketPurchase', () => {
  const validUser = { email: 'user@example.com', name: 'Test User', member: null }
  const memberUser = { email: 'member@example.com', name: 'Member', member: baseMember() }

  it('rejects cancelled event', () => {
    const event = ticketedEvent({ isCancelled: true })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('Event has been cancelled')
  })

  it('rejects past event', () => {
    const event = ticketedEvent({ startDate: pastDate() })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('Event has already started')
  })

  it('rejects when registration deadline has passed', () => {
    const event = ticketedEvent({ registrationDeadline: pastDate() })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('Registration deadline has passed')
  })

  it('rejects already registered user', () => {
    const event = ticketedEvent({
      registrations: [{ email: 'user@example.com', cancelledAt: null }]
    })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('You are already registered for this event')
  })

  it('allows user whose previous registration was cancelled', () => {
    const event = ticketedEvent({
      registrations: [{ email: 'user@example.com', cancelledAt: new Date() }]
    })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(true)
  })

  it('rejects non-member for members-only event', () => {
    const event = ticketedEvent({ membersOnly: true })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toContain('members only')
  })

  it('allows member for members-only event', () => {
    const event = ticketedEvent({ membersOnly: true })
    const result = validateTicketPurchase(event, memberUser)

    expect(result.valid).toBe(true)
    expect(result.ticketInfo).toBeDefined()
    expect(result.availability).toBeDefined()
  })

  it('rejects when no tickets available for user status', () => {
    const event = ticketedEvent({
      tickets: {
        enabled: true,
        member: { available: false },
        public: { available: false }
      }
    })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toContain('No tickets available')
  })

  it('rejects when sold out with waitlist info', () => {
    const event = ticketedEvent()
    event.tickets.public.quantity = 1
    event.tickets.public.sold = 1
    event.tickets.waitlist = { enabled: true }

    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('Event is sold out')
    expect(result.waitlistAvailable).toBe(true)
  })

  it('returns valid with ticket info and availability for good purchase', () => {
    const event = ticketedEvent()
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(true)
    expect(result.ticketInfo.ticketType).toBe('public')
    expect(result.ticketInfo.price).toBe(30)
    expect(result.availability.available).toBe(true)
  })

  it('is case-insensitive on email match for duplicate check', () => {
    const event = ticketedEvent({
      registrations: [{ email: 'USER@EXAMPLE.COM', cancelledAt: null }]
    })
    const result = validateTicketPurchase(event, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('You are already registered for this event')
  })
})

// ===========================================================================
// formatPrice
// ===========================================================================

describe('formatPrice', () => {
  it('returns "Free" for zero price', () => {
    expect(formatPrice(0)).toBe('Free')
  })

  it('formats CAD price by default', () => {
    const result = formatPrice(25)
    expect(result).toContain('25.00')
  })

  it('formats decimal prices', () => {
    const result = formatPrice(9.99)
    expect(result).toContain('9.99')
  })

  it('respects USD currency', () => {
    const result = formatPrice(25, 'USD')
    expect(result).toContain('25.00')
    // US$ or $ prefix depending on locale — just confirm it formats
    expect(result).toMatch(/\$/)
  })
})

// ===========================================================================
// calculateSeriesTicketPrice
// ===========================================================================

describe('calculateSeriesTicketPrice', () => {
  it('returns free guest ticket when tickets not enabled', () => {
    const series = { tickets: { enabled: false } }
    const result = calculateSeriesTicketPrice(series)

    expect(result).toEqual({
      ticketType: 'guest',
      price: 0,
      currency: 'CAD',
      isEarlyBird: false,
      isFree: true
    })
  })

  it('returns member price for authenticated member', () => {
    const series = ticketedSeries()
    const member = baseMember()
    const result = calculateSeriesTicketPrice(series, member)

    expect(result.ticketType).toBe('member')
    expect(result.price).toBe(20)
    expect(result.isFree).toBe(false)
    expect(result.name).toBe('Member Series Pass')
  })

  it('applies circle override to series member price', () => {
    const series = ticketedSeries({
      tickets: {
        enabled: true,
        currency: 'CAD',
        member: {
          available: true,
          price: 20,
          isFree: false,
          circleOverrides: { founder: { price: 10 } }
        },
        public: { available: true, price: 60 }
      }
    })
    const member = baseMember({ circle: 'founder' })
    const result = calculateSeriesTicketPrice(series, member)

    expect(result.price).toBe(10)
  })

  it('returns public price for non-member', () => {
    const series = ticketedSeries()
    const result = calculateSeriesTicketPrice(series, null)

    expect(result.ticketType).toBe('public')
    expect(result.price).toBe(60)
  })

  it('applies early bird pricing before deadline', () => {
    const series = ticketedSeries({
      tickets: {
        enabled: true,
        currency: 'CAD',
        member: { available: false },
        public: {
          available: true,
          price: 60,
          earlyBirdPrice: 40,
          earlyBirdDeadline: futureDate()
        }
      }
    })
    const result = calculateSeriesTicketPrice(series, null)

    expect(result.price).toBe(40)
    expect(result.isEarlyBird).toBe(true)
  })

  it('returns null when no tickets available for user', () => {
    const series = ticketedSeries({
      tickets: {
        enabled: true,
        member: { available: false },
        public: { available: false }
      }
    })
    const result = calculateSeriesTicketPrice(series, null)

    expect(result).toBeNull()
  })
})

// ===========================================================================
// checkSeriesTicketAvailability
// ===========================================================================

describe('checkSeriesTicketAvailability', () => {
  it('returns unavailable when tickets not enabled', () => {
    const series = { tickets: { enabled: false } }
    const result = checkSeriesTicketAvailability(series)

    expect(result.available).toBe(false)
  })

  it('returns available with remaining for public tickets', () => {
    const series = ticketedSeries()
    series.tickets.public.quantity = 30
    series.tickets.public.sold = 10
    series.tickets.public.reserved = 5

    const result = checkSeriesTicketAvailability(series, 'public')

    expect(result.available).toBe(true)
    expect(result.remaining).toBe(15)
  })

  it('returns unavailable when public tickets sold out', () => {
    const series = ticketedSeries()
    series.tickets.public.quantity = 5
    series.tickets.public.sold = 5

    const result = checkSeriesTicketAvailability(series, 'public')

    expect(result.available).toBe(false)
    expect(result.remaining).toBe(0)
  })

  it('returns unavailable when total capacity reached', () => {
    const series = ticketedSeries()
    series.tickets.capacity.total = 2
    series.registrations = [
      { email: 'a@b.com' },
      { email: 'c@d.com' }
    ]

    const result = checkSeriesTicketAvailability(series, 'member')

    expect(result.available).toBe(false)
    expect(result.remaining).toBe(0)
  })

  it('excludes cancelled registrations from count', () => {
    const series = ticketedSeries()
    series.tickets.capacity.total = 2
    series.registrations = [
      { email: 'a@b.com', cancelledAt: null },
      { email: 'c@d.com', cancelledAt: new Date() }
    ]

    const result = checkSeriesTicketAvailability(series, 'member')

    expect(result.available).toBe(true)
    expect(result.remaining).toBe(1)
  })

  it('returns unlimited for member tickets with no capacity', () => {
    const series = ticketedSeries()
    delete series.tickets.capacity

    const result = checkSeriesTicketAvailability(series, 'member')

    expect(result.available).toBe(true)
    expect(result.remaining).toBeNull()
  })
})

// ===========================================================================
// validateSeriesTicketPurchase
// ===========================================================================

describe('validateSeriesTicketPurchase', () => {
  const validUser = { email: 'user@example.com', name: 'Test User', member: null }
  const memberUser = { email: 'member@example.com', name: 'Member', member: baseMember() }

  it('rejects inactive series', () => {
    const series = ticketedSeries({ isActive: false })
    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('This series is not currently available')
  })

  it('rejects already registered user', () => {
    const series = ticketedSeries({
      registrations: [{ email: 'user@example.com', cancelledAt: null }]
    })
    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('You already have a pass for this series')
  })

  it('allows user whose previous registration was cancelled', () => {
    const series = ticketedSeries({
      registrations: [{ email: 'user@example.com', cancelledAt: new Date() }]
    })
    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(true)
  })

  it('rejects when no tickets available for user status', () => {
    const series = ticketedSeries({
      tickets: {
        enabled: true,
        member: { available: false },
        public: { available: false }
      }
    })
    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toContain('No series passes available')
  })

  it('rejects when sold out', () => {
    const series = ticketedSeries()
    series.tickets.public.quantity = 1
    series.tickets.public.sold = 1
    series.tickets.waitlist = { enabled: true }

    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(false)
    expect(result.reason).toBe('Series passes are sold out')
    expect(result.waitlistAvailable).toBe(true)
  })

  it('returns valid with ticket info for good purchase', () => {
    const series = ticketedSeries()
    const result = validateSeriesTicketPurchase(series, validUser)

    expect(result.valid).toBe(true)
    expect(result.ticketInfo.ticketType).toBe('public')
    expect(result.ticketInfo.price).toBe(60)
    expect(result.availability.available).toBe(true)
  })

  it('returns member ticket info for authenticated member', () => {
    const series = ticketedSeries()
    const result = validateSeriesTicketPurchase(series, memberUser)

    expect(result.valid).toBe(true)
    expect(result.ticketInfo.ticketType).toBe('member')
    expect(result.ticketInfo.price).toBe(20)
  })
})

// ===========================================================================
// checkUserSeriesPass
// ===========================================================================

describe('checkUserSeriesPass', () => {
  it('returns hasPass true when user has active registration', () => {
    const series = {
      registrations: [
        { email: 'user@example.com', cancelledAt: null, paymentStatus: 'completed' }
      ]
    }
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(true)
    expect(result.registration).toBeDefined()
    expect(result.registration.email).toBe('user@example.com')
  })

  it('returns hasPass false when no registration exists', () => {
    const series = { registrations: [] }
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(false)
    expect(result.registration).toBeNull()
  })

  it('returns hasPass false when registration is cancelled', () => {
    const series = {
      registrations: [
        { email: 'user@example.com', cancelledAt: new Date(), paymentStatus: 'completed' }
      ]
    }
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(false)
  })

  it('returns hasPass false when payment failed', () => {
    const series = {
      registrations: [
        { email: 'user@example.com', cancelledAt: null, paymentStatus: 'failed' }
      ]
    }
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(false)
  })

  it('is case-insensitive on email', () => {
    const series = {
      registrations: [
        { email: 'USER@EXAMPLE.COM', cancelledAt: null, paymentStatus: 'completed' }
      ]
    }
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(true)
  })

  it('handles missing registrations array', () => {
    const series = {}
    const result = checkUserSeriesPass(series, 'user@example.com')

    expect(result.hasPass).toBe(false)
    expect(result.registration).toBeNull()
  })
})
