import { describe, it, expect } from 'vitest'
import {
  helcimCreatePlanSchema,
  helcimCustomerSchema,
  helcimInitializePaymentSchema,
  helcimSubscriptionSchema,
  helcimUpdateBillingSchema,
  ticketPurchaseSchema,
  ticketReserveSchema,
  ticketEligibilitySchema,
  waitlistSchema,
  waitlistDeleteSchema,
  cancelRegistrationSchema,
  checkRegistrationSchema,
  guestRegisterSchema,
  eventPaymentSchema,
  updateContributionSchema,
  peerSupportUpdateSchema,
  seriesTicketPurchaseSchema,
  seriesTicketEligibilitySchema,
  adminSeriesCreateSchema,
  adminSeriesUpdateSchema,
  adminSeriesItemUpdateSchema,
  adminSeriesTicketsSchema,
  adminEventUpdateSchema,
  adminMemberCreateSchema
} from '../../../server/utils/schemas.js'

// --- Helcim schemas ---

describe('helcimCreatePlanSchema', () => {
  it('accepts valid plan data', () => {
    const result = helcimCreatePlanSchema.safeParse({
      name: 'Monthly Plan',
      amount: '15.00',
      frequency: 'monthly'
    })
    expect(result.success).toBe(true)
  })

  it('accepts numeric amount', () => {
    const result = helcimCreatePlanSchema.safeParse({
      name: 'Plan',
      amount: 15,
      frequency: 'monthly'
    })
    expect(result.success).toBe(true)
  })

  it('rejects missing name', () => {
    const result = helcimCreatePlanSchema.safeParse({
      amount: 15,
      frequency: 'monthly'
    })
    expect(result.success).toBe(false)
  })

  it('rejects missing amount', () => {
    const result = helcimCreatePlanSchema.safeParse({
      name: 'Plan',
      frequency: 'monthly'
    })
    expect(result.success).toBe(false)
  })

  it('strips unknown fields', () => {
    const result = helcimCreatePlanSchema.safeParse({
      name: 'Plan',
      amount: 15,
      frequency: 'monthly',
      malicious: 'data'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('malicious')
  })
})

describe('helcimCustomerSchema', () => {
  it('accepts valid customer data', () => {
    const result = helcimCustomerSchema.safeParse({
      name: 'Jane Doe',
      email: 'jane@example.com'
    })
    expect(result.success).toBe(true)
  })

  it('lowercases email', () => {
    const result = helcimCustomerSchema.safeParse({
      name: 'Jane',
      email: 'JANE@Example.COM'
    })
    expect(result.success).toBe(true)
    expect(result.data.email).toBe('jane@example.com')
  })

  it('rejects invalid email', () => {
    const result = helcimCustomerSchema.safeParse({
      name: 'Jane',
      email: 'not-an-email'
    })
    expect(result.success).toBe(false)
  })

  it('strips role field', () => {
    const result = helcimCustomerSchema.safeParse({
      name: 'Jane',
      email: 'jane@example.com',
      role: 'admin'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('role')
  })
})

describe('helcimSubscriptionSchema', () => {
  it('accepts valid subscription data', () => {
    const result = helcimSubscriptionSchema.safeParse({
      customerId: '12345',
      contributionTier: '15',
      customerCode: 'CST123'
    })
    expect(result.success).toBe(true)
  })

  it('rejects invalid contribution tier', () => {
    const result = helcimSubscriptionSchema.safeParse({
      customerId: '12345',
      contributionTier: '999',
      customerCode: 'CST123'
    })
    expect(result.success).toBe(false)
  })

  it('rejects missing customerCode', () => {
    const result = helcimSubscriptionSchema.safeParse({
      customerId: '12345',
      contributionTier: '15'
    })
    expect(result.success).toBe(false)
  })
})

describe('helcimUpdateBillingSchema', () => {
  const validBilling = {
    customerId: '123',
    billingAddress: {
      street: '123 Main St',
      city: 'Toronto',
      country: 'CA',
      postalCode: 'M5V 1A1'
    }
  }

  it('accepts valid billing data', () => {
    const result = helcimUpdateBillingSchema.safeParse(validBilling)
    expect(result.success).toBe(true)
  })

  it('rejects missing street', () => {
    const result = helcimUpdateBillingSchema.safeParse({
      customerId: '123',
      billingAddress: {
        city: 'Toronto',
        country: 'CA',
        postalCode: 'M5V'
      }
    })
    expect(result.success).toBe(false)
  })

  it('rejects missing billingAddress', () => {
    const result = helcimUpdateBillingSchema.safeParse({
      customerId: '123'
    })
    expect(result.success).toBe(false)
  })
})

// --- Event schemas ---

describe('ticketPurchaseSchema', () => {
  it('accepts valid ticket purchase', () => {
    const result = ticketPurchaseSchema.safeParse({
      name: 'Jane',
      email: 'jane@example.com'
    })
    expect(result.success).toBe(true)
  })

  it('lowercases email', () => {
    const result = ticketPurchaseSchema.safeParse({
      name: 'Jane',
      email: 'JANE@Example.COM'
    })
    expect(result.success).toBe(true)
    expect(result.data.email).toBe('jane@example.com')
  })

  it('rejects missing name', () => {
    const result = ticketPurchaseSchema.safeParse({
      email: 'jane@example.com'
    })
    expect(result.success).toBe(false)
  })

  it('strips unknown fields', () => {
    const result = ticketPurchaseSchema.safeParse({
      name: 'Jane',
      email: 'jane@example.com',
      role: 'admin',
      status: 'active'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('role')
    expect(result.data).not.toHaveProperty('status')
  })
})

describe('ticketReserveSchema', () => {
  it('accepts valid email', () => {
    const result = ticketReserveSchema.safeParse({ email: 'test@example.com' })
    expect(result.success).toBe(true)
  })

  it('rejects missing email', () => {
    const result = ticketReserveSchema.safeParse({})
    expect(result.success).toBe(false)
  })
})

describe('waitlistSchema', () => {
  it('accepts valid waitlist entry', () => {
    const result = waitlistSchema.safeParse({
      email: 'test@example.com',
      name: 'Test User'
    })
    expect(result.success).toBe(true)
  })

  it('accepts email-only entry', () => {
    const result = waitlistSchema.safeParse({ email: 'test@example.com' })
    expect(result.success).toBe(true)
  })

  it('rejects missing email', () => {
    const result = waitlistSchema.safeParse({ name: 'Test' })
    expect(result.success).toBe(false)
  })
})

describe('guestRegisterSchema', () => {
  it('accepts valid guest data', () => {
    const result = guestRegisterSchema.safeParse({
      name: 'Guest User',
      email: 'guest@example.com'
    })
    expect(result.success).toBe(true)
  })

  it('rejects missing name', () => {
    const result = guestRegisterSchema.safeParse({ email: 'guest@example.com' })
    expect(result.success).toBe(false)
  })
})

describe('eventPaymentSchema', () => {
  it('accepts valid payment data', () => {
    const result = eventPaymentSchema.safeParse({
      name: 'Payer',
      email: 'payer@example.com',
      paymentToken: 'tok_abc123'
    })
    expect(result.success).toBe(true)
  })

  it('rejects missing paymentToken', () => {
    const result = eventPaymentSchema.safeParse({
      name: 'Payer',
      email: 'payer@example.com'
    })
    expect(result.success).toBe(false)
  })
})

// --- Member schemas ---

describe('updateContributionSchema', () => {
  it('accepts valid contribution tier', () => {
    const result = updateContributionSchema.safeParse({ contributionTier: '15' })
    expect(result.success).toBe(true)
  })

  it('rejects invalid tier', () => {
    const result = updateContributionSchema.safeParse({ contributionTier: '100' })
    expect(result.success).toBe(false)
  })

  it('strips unknown fields', () => {
    const result = updateContributionSchema.safeParse({
      contributionTier: '15',
      role: 'admin'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('role')
  })
})

describe('peerSupportUpdateSchema', () => {
  it('accepts valid peer support data', () => {
    const result = peerSupportUpdateSchema.safeParse({
      enabled: true,
      skillTopics: ['game design', 'business'],
      slackUsername: 'jane'
    })
    expect(result.success).toBe(true)
  })

  it('accepts empty object', () => {
    const result = peerSupportUpdateSchema.safeParse({})
    expect(result.success).toBe(true)
  })

  it('rejects non-array skillTopics', () => {
    const result = peerSupportUpdateSchema.safeParse({
      skillTopics: 'not-an-array'
    })
    expect(result.success).toBe(false)
  })
})

// --- Series schemas ---

describe('seriesTicketPurchaseSchema', () => {
  it('accepts valid series ticket purchase', () => {
    const result = seriesTicketPurchaseSchema.safeParse({
      name: 'Buyer',
      email: 'buyer@example.com'
    })
    expect(result.success).toBe(true)
  })

  it('rejects missing name', () => {
    const result = seriesTicketPurchaseSchema.safeParse({
      email: 'buyer@example.com'
    })
    expect(result.success).toBe(false)
  })
})

// --- Admin schemas ---

describe('adminSeriesCreateSchema', () => {
  it('accepts valid series', () => {
    const result = adminSeriesCreateSchema.safeParse({
      id: 'test-series',
      title: 'Test Series',
      description: 'A test series'
    })
    expect(result.success).toBe(true)
  })

  it('rejects missing id', () => {
    const result = adminSeriesCreateSchema.safeParse({
      title: 'Test',
      description: 'Desc'
    })
    expect(result.success).toBe(false)
  })
})

describe('adminMemberCreateSchema', () => {
  it('accepts valid admin member create', () => {
    const result = adminMemberCreateSchema.safeParse({
      name: 'Admin Created',
      email: 'admin-created@example.com',
      circle: 'founder',
      contributionTier: '30'
    })
    expect(result.success).toBe(true)
  })

  it('strips role field (mass assignment)', () => {
    const result = adminMemberCreateSchema.safeParse({
      name: 'Admin Created',
      email: 'admin-created@example.com',
      circle: 'founder',
      contributionTier: '30',
      role: 'admin'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('role')
  })

  it('strips status field (mass assignment)', () => {
    const result = adminMemberCreateSchema.safeParse({
      name: 'Admin Created',
      email: 'admin-created@example.com',
      circle: 'founder',
      contributionTier: '30',
      status: 'active'
    })
    expect(result.success).toBe(true)
    expect(result.data).not.toHaveProperty('status')
  })

  it('rejects invalid circle', () => {
    const result = adminMemberCreateSchema.safeParse({
      name: 'Admin Created',
      email: 'admin-created@example.com',
      circle: 'superadmin',
      contributionTier: '30'
    })
    expect(result.success).toBe(false)
  })
})

describe('adminEventUpdateSchema', () => {
  const validUpdate = {
    title: 'Updated Event',
    description: 'Updated description',
    startDate: '2026-04-01T10:00:00Z',
    endDate: '2026-04-01T12:00:00Z'
  }

  it('accepts valid event update', () => {
    const result = adminEventUpdateSchema.safeParse(validUpdate)
    expect(result.success).toBe(true)
  })

  it('rejects missing title', () => {
    const { title, ...rest } = validUpdate
    const result = adminEventUpdateSchema.safeParse(rest)
    expect(result.success).toBe(false)
  })

  it('accepts optional tickets', () => {
    const result = adminEventUpdateSchema.safeParse({
      ...validUpdate,
      tickets: {
        enabled: true,
        public: {
          available: true,
          price: 25.00
        }
      }
    })
    expect(result.success).toBe(true)
  })
})

// --- Error text forwarding regression tests ---

describe('error text forwarding regression', () => {
  const helcimFiles = [
    'create-plan.post.js',
    'customer.post.js',
    'customer-code.get.js',
    'initialize-payment.post.js',
    'subscription.post.js',
    'update-billing.post.js',
    'get-or-create-customer.post.js'
  ]

  for (const file of helcimFiles) {
    it(`${file} does not forward error text to client`, () => {
      const source = readFileSync(
        resolve(import.meta.dirname, `../../../server/api/helcim/${file}`),
        'utf-8'
      )
      // Should not contain template literals that interpolate errorText or error.message into statusMessage
      expect(source).not.toMatch(/statusMessage:.*\$\{errorText\}/)
      expect(source).not.toMatch(/statusMessage:\s*error\.message\b/)
    })
  }

  it('members/update-contribution.post.js does not forward error text', () => {
    const source = readFileSync(
      resolve(import.meta.dirname, '../../../server/api/members/update-contribution.post.js'),
      'utf-8'
    )
    expect(source).not.toMatch(/statusMessage:.*\$\{errorText\}/)
    expect(source).not.toMatch(/statusMessage:\s*error\.message\b/)
  })
})

import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'

// --- validateBody migration coverage ---

describe('validateBody migration coverage', () => {
  const endpoints = [
    'helcim/create-plan.post.js',
    'helcim/customer.post.js',
    'helcim/initialize-payment.post.js',
    'helcim/subscription.post.js',
    'helcim/update-billing.post.js',
    'events/[id]/tickets/purchase.post.js',
    'events/[id]/tickets/reserve.post.js',
    'events/[id]/tickets/check-eligibility.post.js',
    'events/[id]/waitlist.post.js',
    'events/[id]/waitlist.delete.js',
    'events/[id]/cancel-registration.post.js',
    'events/[id]/check-registration.post.js',
    'events/[id]/guest-register.post.js',
    'events/[id]/payment.post.js',
    'members/update-contribution.post.js',
    'members/me/peer-support.patch.js',
    'series/[id]/tickets/purchase.post.js',
    'series/[id]/tickets/check-eligibility.post.js',
    'admin/series.post.js',
    'admin/series.put.js',
    'admin/series/tickets.put.js',
    'admin/series/[id].put.js',
    'admin/events/[id].put.js',
    'admin/members.post.js'
  ]

  for (const endpoint of endpoints) {
    it(`${endpoint} uses validateBody instead of raw readBody`, () => {
      const source = readFileSync(
        resolve(import.meta.dirname, `../../../server/api/${endpoint}`),
        'utf-8'
      )
      expect(source).toContain('validateBody(event')
      // Should not have bare readBody calls (except inside validateBody itself)
      const lines = source.split('\n')
      for (const line of lines) {
        if (line.includes('readBody(event)') && !line.includes('validateBody')) {
          expect.fail(`${endpoint} still has raw readBody: ${line.trim()}`)
        }
      }
    })
  }
})
