import { describe, it, expect } from 'vitest'
import {
  CreateScheduleRequestSchema,
  ListSchedulesQuerySchema,
  GetScheduleParamsSchema,
  UpdateAnchorRequestSchema,
  ProcessSchedulesBatchRequestSchema
} from '../api-schemas'
import { TaskScheduleConfigSchema } from '../schemas'

describe('TaskScheduleConfigSchema', () => {
  describe('RecurringScheduleConfig', () => {
    it('validates recurring schedule with cron', () => {
      const valid = {
        type: 'recurring',
        cron: '0 9 * * 1',
        timezone: 'America/New_York',
        payload: { report_type: 'weekly' }
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates recurring schedule with interval', () => {
      const valid = {
        type: 'recurring',
        interval: 'daily',
        time: '09:00',
        timezone: 'America/New_York',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates recurring schedule with weekly interval', () => {
      const valid = {
        type: 'recurring',
        interval: 'weekly',
        time: '14:30',
        timezone: 'UTC',
        payload: { frequency: 'weekly' }
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates recurring schedule with monthly interval', () => {
      const valid = {
        type: 'recurring',
        interval: 'monthly',
        time: '08:00',
        timezone: 'Europe/London',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates recurring schedule with endAt', () => {
      const valid = {
        type: 'recurring',
        interval: 'daily',
        time: '09:00',
        timezone: 'UTC',
        payload: {},
        endAt: '2026-12-31T23:59:59Z'
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates recurring schedule with null endAt', () => {
      const valid = {
        type: 'recurring',
        interval: 'daily',
        time: '09:00',
        timezone: 'UTC',
        payload: {},
        endAt: null
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('rejects recurring schedule without cron or interval', () => {
      const invalid = {
        type: 'recurring',
        timezone: 'America/New_York',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects recurring schedule with empty timezone', () => {
      const invalid = {
        type: 'recurring',
        interval: 'daily',
        time: '09:00',
        timezone: '',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects recurring schedule with invalid time format', () => {
      const invalid = {
        type: 'recurring',
        interval: 'daily',
        time: '9:00', // Should be HH:MM format
        timezone: 'UTC',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects recurring schedule with invalid interval', () => {
      const invalid = {
        type: 'recurring',
        interval: 'yearly', // Not a valid interval
        time: '09:00',
        timezone: 'UTC',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })
  })

  describe('RelativeScheduleConfig', () => {
    it('validates relative schedule with items', () => {
      const valid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [
          { offset: '+3d', payload: { tone: 'friendly' } },
          { offset: '+7d', payload: { tone: 'casual' }, label: 'Follow up' }
        ]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates relative schedule with negative offsets', () => {
      const valid = {
        type: 'relative',
        anchorAt: '2026-06-01T00:00:00Z',
        anchorLabel: 'Product Launch',
        items: [
          { offset: '-30d', payload: { reminder: '30 days out' } },
          { offset: '-7d', payload: { reminder: '1 week out' } },
          { offset: '-1d', payload: { reminder: 'tomorrow' } }
        ]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates relative schedule with hour offsets', () => {
      const valid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [
          { offset: '+2h', payload: {} },
          { offset: '-4h', payload: {} }
        ]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates relative schedule with minute offsets', () => {
      const valid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [
          { offset: '+30m', payload: {} },
          { offset: '-15m', payload: {} }
        ]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('rejects relative schedule with empty items', () => {
      const invalid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: []
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects invalid offset format - no sign', () => {
      const invalid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [{ offset: '3d', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects invalid offset format - invalid unit', () => {
      const invalid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [{ offset: '+3w', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects invalid offset format - spelled out', () => {
      const invalid = {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [{ offset: '3 days', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects invalid anchor datetime', () => {
      const invalid = {
        type: 'relative',
        anchorAt: 'not-a-datetime',
        items: [{ offset: '+1d', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })
  })

  describe('AbsoluteScheduleConfig', () => {
    it('validates absolute schedule with items', () => {
      const valid = {
        type: 'absolute',
        items: [
          { runAt: '2026-01-15T09:00:00Z', payload: { phase: 1 } },
          { runAt: '2026-02-01T09:00:00Z', payload: { phase: 2 }, label: 'Phase 2' }
        ]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('validates absolute schedule with single item', () => {
      const valid = {
        type: 'absolute',
        items: [{ runAt: '2026-03-15T14:30:00Z', payload: { event: 'launch' } }]
      }
      expect(() => TaskScheduleConfigSchema.parse(valid)).not.toThrow()
    })

    it('rejects absolute schedule with empty items', () => {
      const invalid = {
        type: 'absolute',
        items: []
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects invalid datetime format', () => {
      const invalid = {
        type: 'absolute',
        items: [{ runAt: 'not-a-date', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })

    it('rejects datetime without timezone', () => {
      const invalid = {
        type: 'absolute',
        items: [{ runAt: '2026-01-15T09:00:00', payload: {} }]
      }
      expect(() => TaskScheduleConfigSchema.parse(invalid)).toThrow()
    })
  })

  describe('overduePolicy field', () => {
    it('accepts valid overduePolicy values on recurring schedule', () => {
      const policies = ['skip', 'execute'] as const
      policies.forEach((policy) => {
        const config = {
          type: 'recurring',
          interval: 'daily',
          time: '09:00',
          timezone: 'UTC',
          payload: {},
          overduePolicy: policy
        }
        expect(() => TaskScheduleConfigSchema.parse(config)).not.toThrow()
      })
    })

    it('accepts valid overduePolicy values on relative schedule', () => {
      const policies = ['skip', 'execute'] as const
      policies.forEach((policy) => {
        const config = {
          type: 'relative',
          anchorAt: '2026-01-15T10:00:00Z',
          items: [{ offset: '+1d', payload: {} }],
          overduePolicy: policy
        }
        expect(() => TaskScheduleConfigSchema.parse(config)).not.toThrow()
      })
    })

    it('accepts valid overduePolicy values on absolute schedule', () => {
      const policies = ['skip', 'execute'] as const
      policies.forEach((policy) => {
        const config = {
          type: 'absolute',
          items: [{ runAt: '2026-01-15T10:00:00Z', payload: {} }],
          overduePolicy: policy
        }
        expect(() => TaskScheduleConfigSchema.parse(config)).not.toThrow()
      })
    })

    it('accepts missing overduePolicy (optional field)', () => {
      const config = {
        type: 'recurring',
        interval: 'daily',
        time: '09:00',
        timezone: 'UTC',
        payload: {}
      }
      expect(() => TaskScheduleConfigSchema.parse(config)).not.toThrow()
    })

    it('rejects invalid overduePolicy values', () => {
      const invalidPolicies = ['ignore', 'delay', 'queue', 'retry', '']
      invalidPolicies.forEach((policy) => {
        const config = {
          type: 'recurring',
          interval: 'daily',
          time: '09:00',
          timezone: 'UTC',
          payload: {},
          overduePolicy: policy
        }
        expect(() => TaskScheduleConfigSchema.parse(config)).toThrow()
      })
    })
  })
})

describe('CreateScheduleRequestSchema', () => {
  const validRecurringSchedule = {
    name: 'Weekly Report',
    description: 'Generates weekly summary',
    target: { resourceType: 'agent', resourceId: 'agent-123' },
    scheduleConfig: {
      type: 'recurring',
      interval: 'weekly',
      time: '09:00',
      timezone: 'UTC',
      payload: {}
    }
  }

  it('validates complete create request with recurring schedule', () => {
    expect(() => CreateScheduleRequestSchema.parse(validRecurringSchedule)).not.toThrow()
  })

  it('validates create request with relative schedule', () => {
    const valid = {
      name: 'Onboarding Sequence',
      target: { resourceType: 'workflow', resourceId: 'workflow-456' },
      scheduleConfig: {
        type: 'relative',
        anchorAt: '2026-01-15T10:00:00Z',
        items: [
          { offset: '+1d', payload: { step: 1 } },
          { offset: '+3d', payload: { step: 2 } }
        ]
      }
    }
    expect(() => CreateScheduleRequestSchema.parse(valid)).not.toThrow()
  })

  it('validates create request with absolute schedule', () => {
    const valid = {
      name: 'Product Launch Campaign',
      target: { resourceType: 'agent', resourceId: 'campaign-agent' },
      scheduleConfig: {
        type: 'absolute',
        items: [
          { runAt: '2026-03-01T09:00:00Z', payload: { phase: 'teaser' } },
          { runAt: '2026-03-15T09:00:00Z', payload: { phase: 'launch' } }
        ]
      }
    }
    expect(() => CreateScheduleRequestSchema.parse(valid)).not.toThrow()
  })

  it('validates create request with optional fields', () => {
    const valid = {
      ...validRecurringSchedule,
      maxRetries: 5,
      idempotencyKey: 'unique-key-123',
      originExecutionId: '123e4567-e89b-12d3-a456-426614174000',
      originResourceType: 'scheduler',
      originResourceId: 'schedule-abc'
    }
    expect(() => CreateScheduleRequestSchema.parse(valid)).not.toThrow()
  })

  it('rejects missing name', () => {
    const { name: _name, ...invalid } = validRecurringSchedule
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects empty name', () => {
    const invalid = { ...validRecurringSchedule, name: '' }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects name exceeding max length', () => {
    const invalid = { ...validRecurringSchedule, name: 'a'.repeat(256) }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects missing target', () => {
    const { target: _target, ...invalid } = validRecurringSchedule
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects empty target resourceId', () => {
    const invalid = {
      ...validRecurringSchedule,
      target: { resourceType: 'agent', resourceId: '' }
    }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects invalid target resourceType', () => {
    const invalid = {
      ...validRecurringSchedule,
      target: { resourceType: 'invalid', resourceId: 'test-123' }
    }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects maxRetries > 10', () => {
    const invalid = { ...validRecurringSchedule, maxRetries: 11 }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects maxRetries < 0', () => {
    const invalid = { ...validRecurringSchedule, maxRetries: -1 }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects invalid originExecutionId UUID', () => {
    const invalid = { ...validRecurringSchedule, originExecutionId: 'not-a-uuid' }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('rejects invalid originResourceType', () => {
    const invalid = { ...validRecurringSchedule, originResourceType: 'invalid-type' }
    expect(() => CreateScheduleRequestSchema.parse(invalid)).toThrow()
  })

  it('accepts all valid resource types for target', () => {
    const resourceTypes = ['agent', 'workflow']
    resourceTypes.forEach((resourceType) => {
      const valid = {
        ...validRecurringSchedule,
        target: { resourceType, resourceId: 'test-123' }
      }
      expect(() => CreateScheduleRequestSchema.parse(valid)).not.toThrow()
    })
  })

  it('accepts all valid origin resource types', () => {
    const originTypes = ['agent', 'workflow', 'scheduler', 'api']
    originTypes.forEach((originResourceType) => {
      const valid = {
        ...validRecurringSchedule,
        originResourceType,
        originResourceId: 'test-origin'
      }
      expect(() => CreateScheduleRequestSchema.parse(valid)).not.toThrow()
    })
  })
})

describe('ListSchedulesQuerySchema', () => {
  it('accepts empty query (all defaults)', () => {
    const result = ListSchedulesQuerySchema.safeParse({})
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.limit).toBe(50)
      expect(result.data.offset).toBe(0)
    }
  })

  it('accepts valid status filter', () => {
    const result = ListSchedulesQuerySchema.safeParse({ status: 'active' })
    expect(result.success).toBe(true)
  })

  it('accepts all valid status values', () => {
    const statuses = ['active', 'paused', 'completed', 'cancelled']
    statuses.forEach((status) => {
      const result = ListSchedulesQuerySchema.safeParse({ status })
      expect(result.success).toBe(true)
    })
  })

  it('rejects invalid status', () => {
    const result = ListSchedulesQuerySchema.safeParse({ status: 'pending' })
    expect(result.success).toBe(false)
  })

  it('accepts valid targetResourceType', () => {
    const result = ListSchedulesQuerySchema.safeParse({ targetResourceType: 'agent' })
    expect(result.success).toBe(true)
  })

  it('accepts targetResourceId filter', () => {
    const result = ListSchedulesQuerySchema.safeParse({ targetResourceId: 'agent-123' })
    expect(result.success).toBe(true)
  })

  it('coerces string limit to number', () => {
    const result = ListSchedulesQuerySchema.safeParse({ limit: '25' })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.limit).toBe(25)
    }
  })

  it('coerces string offset to number', () => {
    const result = ListSchedulesQuerySchema.safeParse({ offset: '10' })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.offset).toBe(10)
    }
  })

  it('rejects limit > 100', () => {
    const result = ListSchedulesQuerySchema.safeParse({ limit: 101 })
    expect(result.success).toBe(false)
  })

  it('rejects limit < 1', () => {
    const result = ListSchedulesQuerySchema.safeParse({ limit: 0 })
    expect(result.success).toBe(false)
  })

  it('rejects negative offset', () => {
    const result = ListSchedulesQuerySchema.safeParse({ offset: -1 })
    expect(result.success).toBe(false)
  })

  it('accepts combined filters', () => {
    const result = ListSchedulesQuerySchema.safeParse({
      status: 'active',
      targetResourceType: 'workflow',
      targetResourceId: 'workflow-456',
      limit: 20,
      offset: 40
    })
    expect(result.success).toBe(true)
  })
})

describe('GetScheduleParamsSchema', () => {
  it('accepts valid UUID', () => {
    const result = GetScheduleParamsSchema.safeParse({
      id: '123e4567-e89b-12d3-a456-426614174000'
    })
    expect(result.success).toBe(true)
  })

  it('rejects non-UUID string', () => {
    const result = GetScheduleParamsSchema.safeParse({ id: 'not-a-uuid' })
    expect(result.success).toBe(false)
  })

  it('rejects empty string', () => {
    const result = GetScheduleParamsSchema.safeParse({ id: '' })
    expect(result.success).toBe(false)
  })

  it('rejects SQL injection attempt', () => {
    const result = GetScheduleParamsSchema.safeParse({
      id: "'; DELETE FROM task_schedules;--"
    })
    expect(result.success).toBe(false)
  })
})

describe('UpdateAnchorRequestSchema', () => {
  it('accepts valid ISO 8601 datetime', () => {
    const result = UpdateAnchorRequestSchema.safeParse({
      anchorAt: '2026-06-15T10:00:00Z'
    })
    expect(result.success).toBe(true)
  })

  it('rejects datetime with timezone offset (requires Z suffix)', () => {
    // Zod's datetime() validation expects ISO 8601 with Z suffix
    const result = UpdateAnchorRequestSchema.safeParse({
      anchorAt: '2026-06-15T10:00:00+05:00'
    })
    expect(result.success).toBe(false)
  })

  it('rejects invalid datetime format', () => {
    const result = UpdateAnchorRequestSchema.safeParse({
      anchorAt: 'not-a-datetime'
    })
    expect(result.success).toBe(false)
  })

  it('rejects datetime without timezone', () => {
    const result = UpdateAnchorRequestSchema.safeParse({
      anchorAt: '2026-06-15T10:00:00'
    })
    expect(result.success).toBe(false)
  })

  it('rejects date-only format', () => {
    const result = UpdateAnchorRequestSchema.safeParse({
      anchorAt: '2026-06-15'
    })
    expect(result.success).toBe(false)
  })
})

describe('ProcessSchedulesBatchRequestSchema', () => {
  const validScheduleRow = {
    id: '123e4567-e89b-12d3-a456-426614174000',
    organization_id: '123e4567-e89b-12d3-a456-426614174001',
    name: 'Test Schedule',
    target_resource_type: 'agent',
    target_resource_id: 'agent-123',
    schedule_config: {
      type: 'recurring',
      interval: 'daily',
      time: '09:00',
      timezone: 'UTC',
      payload: {}
    },
    current_step: 0,
    status: 'active',
    next_run_at: '2026-01-15T09:00:00Z',
    last_run_at: null,
    last_execution_id: null,
    max_retries: 3,
    retry_count: 0,
    idempotency_key: null,
    description: null,
    origin_execution_id: null,
    origin_resource_type: null,
    origin_resource_id: null,
    created_at: '2026-01-10T00:00:00Z',
    updated_at: '2026-01-10T00:00:00Z'
  }

  it('accepts valid batch with single schedule', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [validScheduleRow]
    })
    expect(result.success).toBe(true)
  })

  it('accepts batch with multiple schedules', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [validScheduleRow, { ...validScheduleRow, id: '123e4567-e89b-12d3-a456-426614174999' }]
    })
    expect(result.success).toBe(true)
  })

  it('accepts schedule with origin tracking', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [
        {
          ...validScheduleRow,
          origin_execution_id: '123e4567-e89b-12d3-a456-426614174002',
          origin_resource_type: 'scheduler',
          origin_resource_id: 'parent-schedule-123'
        }
      ]
    })
    expect(result.success).toBe(true)
  })

  it('rejects empty batch', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({ schedules: [] })
    expect(result.success).toBe(false)
  })

  it('rejects batch > 100 schedules', () => {
    const schedules = Array(101)
      .fill(validScheduleRow)
      .map((schedule, i) => ({
        ...schedule,
        id: `123e4567-e89b-12d3-a456-${String(i).padStart(12, '0')}`
      }))
    const result = ProcessSchedulesBatchRequestSchema.safeParse({ schedules })
    expect(result.success).toBe(false)
  })

  it('accepts batch with exactly 100 schedules', () => {
    const schedules = Array(100)
      .fill(validScheduleRow)
      .map((schedule, i) => ({
        ...schedule,
        id: `123e4567-e89b-12d3-a456-${String(i).padStart(12, '0')}`
      }))
    const result = ProcessSchedulesBatchRequestSchema.safeParse({ schedules })
    expect(result.success).toBe(true)
  })

  it('rejects schedule with invalid id UUID', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [{ ...validScheduleRow, id: 'not-a-uuid' }]
    })
    expect(result.success).toBe(false)
  })

  it('rejects schedule with invalid organization_id UUID', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [{ ...validScheduleRow, organization_id: 'not-a-uuid' }]
    })
    expect(result.success).toBe(false)
  })

  it('rejects schedule with invalid origin_execution_id UUID', () => {
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [{ ...validScheduleRow, origin_execution_id: 'not-a-uuid' }]
    })
    expect(result.success).toBe(false)
  })

  it('accepts negative current_step (edge case)', () => {
    // This tests the schema; business logic would handle negatives separately
    const result = ProcessSchedulesBatchRequestSchema.safeParse({
      schedules: [{ ...validScheduleRow, current_step: -1 }]
    })
    expect(result.success).toBe(true)
  })
})
