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

vi.mock('../lib/prisma', () => ({
  prisma: {
    customer: {
      findFirstOrThrow: vi.fn(),
      findUniqueOrThrow: vi.fn(),
      update: vi.fn(),
      updateMany: vi.fn(),
    },
  },
}))

import { prisma } from '../lib/prisma'
import {
  validateAndFlagLicense,
  validateLicense,
  validateLicenseDate,
  parseExpirationDate,
  ErrorCode,
} from './licenseValidationService'

const NOW = new Date('2026-06-09T12:00:00.000Z')

describe('licenseValidationService', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.useFakeTimers()
    vi.setSystemTime(NOW)
  })

  afterEach(() => {
    vi.useRealTimers()
  })

  // ─── validateLicense (legacy) ────────────────────────────────────────

  it('treats missing license expiry as valid but explicitly records that no date exists', () => {
    expect(validateLicense(null)).toEqual({
      status: 'VALID',
      daysUntilExpiry: null,
      requiresApproval: false,
      message: 'No expiry date on record',
    })
  })

  it('requires approval for licenses expiring inside the 30-day risk window', () => {
    const result = validateLicense(new Date('2026-07-04T12:00:00.000Z'))

    expect(result.status).toBe('EXPIRING')
    expect(result.daysUntilExpiry).toBe(25)
    expect(result.requiresApproval).toBe(true)
    expect(result.message).toContain('License expires in 25 day(s)')
  })

  it('marks exactly 30 days as valid (not expiring)', () => {
    // 2026-07-09 is exactly 30 days from 2026-06-09
    const result = validateLicense(new Date('2026-07-09T12:00:00.000Z'))

    expect(result.status).toBe('VALID')
    expect(result.daysUntilExpiry).toBe(30)
    expect(result.requiresApproval).toBe(false)
  })

  it('requires approval for already expired licenses and reports negative days until expiry', () => {
    const result = validateLicense(new Date('2026-06-04T12:00:00.000Z'))

    expect(result.status).toBe('EXPIRED')
    expect(result.daysUntilExpiry).toBe(-5)
    expect(result.requiresApproval).toBe(true)
  })

  it('persists customer flags for risky licenses without clearing existing safe-customer metadata unnecessarily', async () => {
    vi.mocked(prisma.customer.findFirstOrThrow).mockResolvedValue({
      id: 'customer_1',
      licenseExpiry: new Date('2026-06-20T12:00:00.000Z'),
      flagged: false,
      flagReason: null,
    } as never)

    const result = await validateAndFlagLicense('customer_1')

    expect(result.status).toBe('EXPIRING')
    expect(prisma.customer.update).toHaveBeenCalledWith({
      where: { id: 'customer_1' },
      data: expect.objectContaining({
        licenseExpired: false,
        licenseExpiringSoon: true,
        licenseValidationStatus: 'EXPIRING',
        flagged: true,
        flagReason: expect.stringContaining('License expires in 11 day(s)'),
      }),
    })
  })

  // ─── validateLicenseDate (new public API) ────────────────────────────

  it('rejects missing date', () => {
    const result = validateLicenseDate(null)
    expect(result.valid).toBe(false)
    expect(result.error_code).toBe(ErrorCode.LICENSE_MISSING_DATE)
  })

  it('rejects undefined date', () => {
    const result = validateLicenseDate(undefined)
    expect(result.valid).toBe(false)
    expect(result.error_code).toBe(ErrorCode.LICENSE_MISSING_DATE)
  })

  it('rejects already expired license', () => {
    const result = validateLicenseDate(new Date('2026-06-04T12:00:00.000Z'))
    expect(result.valid).toBe(false)
    expect(result.error_code).toBe(ErrorCode.LICENSE_EXPIRED)
    expect(result.days_remaining).toBe(-5)
  })

  it('rejects license expiring in less than 30 days', () => {
    const result = validateLicenseDate(new Date('2026-06-20T12:00:00.000Z'))
    expect(result.valid).toBe(false)
    expect(result.error_code).toBe(ErrorCode.LICENSE_EXPIRING_SOON)
    expect(result.days_remaining).toBe(11)
  })

  it('accepts license with exactly 30 days remaining', () => {
    const result = validateLicenseDate(new Date('2026-07-09T12:00:00.000Z'))
    expect(result.valid).toBe(true)
    expect(result.days_remaining).toBe(30)
  })

  it('accepts license with more than 30 days remaining', () => {
    const result = validateLicenseDate(new Date('2027-06-09T12:00:00.000Z'))
    expect(result.valid).toBe(true)
    expect(result.days_remaining).toBe(365)
    expect(result.message).toBe('License verified successfully')
  })

  it('rejects future date more than 20 years from now', () => {
    const result = validateLicenseDate(new Date('2050-06-09T12:00:00.000Z'))
    expect(result.valid).toBe(false)
    expect(result.error_code).toBe(ErrorCode.LICENSE_FUTURE_DATE)
  })

  it('handles leap year date correctly', () => {
    const result = validateLicenseDate(new Date('2028-02-29T12:00:00.000Z'))
    expect(result.valid).toBe(true)
    // 2028-02-29 minus 2026-06-09 = should be > 30 days
    expect(result.days_remaining).toBeGreaterThan(600)
  })

  // ─── parseExpirationDate ─────────────────────────────────────────────

  it('parses ISO 8601 date (YYYY-MM-DD)', () => {
    const d = parseExpirationDate('2027-06-11')
    expect(d).toBeInstanceOf(Date)
    expect(d!.getUTCFullYear()).toBe(2027)
    expect(d!.getUTCMonth()).toBe(5) // June
    expect(d!.getUTCDate()).toBe(11)
  })

  it('parses US format (MM/DD/YYYY)', () => {
    const d = parseExpirationDate('06/11/2027')
    expect(d).toBeInstanceOf(Date)
    expect(d!.getFullYear()).toBe(2027)
    expect(d!.getMonth()).toBe(5) // June
    expect(d!.getDate()).toBe(11)
  })

  it('parses DD/MM/YYYY when first part > 12', () => {
    const d = parseExpirationDate('13/06/2027')
    expect(d).toBeInstanceOf(Date)
    expect(d!.getFullYear()).toBe(2027)
    expect(d!.getMonth()).toBe(5) // June
    expect(d!.getDate()).toBe(13)
  })

  it('parses DD-MMM-YYYY (military format)', () => {
    const d = parseExpirationDate('11-JUN-2027')
    expect(d).toBeInstanceOf(Date)
    expect(d!.getFullYear()).toBe(2027)
    expect(d!.getMonth()).toBe(5) // June
    expect(d!.getDate()).toBe(11)
  })

  it('returns null for completely invalid date string', () => {
    expect(parseExpirationDate('not-a-date')).toBeNull()
  })

  it('returns null for empty string', () => {
    expect(parseExpirationDate('')).toBeNull()
  })

  it('returns null for null input', () => {
    expect(parseExpirationDate(null)).toBeNull()
  })

  it('returns null for undefined input', () => {
    expect(parseExpirationDate(undefined)).toBeNull()
  })
})
