/**
 * Zod validation schemas for the projects module.
 * Shared between zync-api route handlers and zync-app React forms.
 */
import { z } from 'zod'

const projectStatusSchema = z.enum(['active', 'on_hold', 'completed', 'archived'])

// ── Billing config schemas (discriminated union) ─────────────────────────────

export const fixedBillingConfigSchema = z.object({
  total_amount: z.number().positive(),
  deposit_pct: z.number().min(0).max(100),
  auto_create_deposit: z.boolean().optional(),
})

export const hourlyBillingConfigSchema = z.object({
  rate_per_hour: z.number().positive(),
  overtime_enabled: z.boolean(),
  overtime_threshold_hours: z.number().positive(),
  overtime_multiplier: z.number().positive(),
  budget_hours: z.number().positive().nullable().optional(),
  budget_alert_pct: z.number().int().min(1).max(100).optional(),
  budget_alert_email: z.boolean().optional(),
  budget_alert_inapp: z.boolean().optional(),
  budget_alert_fired_at: z.string().datetime().nullable().optional(),
})

export const retainerBillingConfigSchema = z.object({
  monthly_amount: z.number().positive(),
  monthly_hours_included: z.number().positive(),
  auto_invoice: z.boolean(),
  hour_bank_overflow_action: z.enum(['invoice', 'carry_over']),
  auto_send_invoice: z.boolean().optional(),
})

/**
 * Discriminated union keyed on the billing_type sibling field.
 * createProjectSchema uses this to validate billing_config based on billing_type.
 */
export const billingConfigByTypeSchema = z.discriminatedUnion('billing_type', [
  z.object({
    billing_type: z.literal('fixed'),
    billing_config: fixedBillingConfigSchema,
  }),
  z.object({
    billing_type: z.literal('hourly'),
    billing_config: hourlyBillingConfigSchema,
  }),
  z.object({
    billing_type: z.literal('retainer'),
    billing_config: retainerBillingConfigSchema,
  }),
])

// ── Project CRUD schemas ──────────────────────────────────────────────────────

const projectBaseSchema = z.object({
  name: z.string().min(1),
  customer_id: z.string().uuid().nullable().optional(),
  description: z.string().nullable().optional(),
  currency: z.string().min(1).max(10).default('ILS').optional(),
  start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
  end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
})

export const addMemberSchema = z.object({
  user_id: z.string().uuid(),
  role: z.enum(['owner', 'member', 'viewer']).default('member').optional(),
  hourly_rate: z.number().positive().nullable().optional(),
})

export const updateMemberSchema = z.object({
  role: z.enum(['owner', 'member', 'viewer']).optional(),
  hourly_rate: z.number().positive().nullable().optional(),
})

/**
 * Create schema: billing_type + billing_config validated as a discriminated union
 * so each billing type's required fields are enforced server-side.
 */
export const createProjectSchema = projectBaseSchema
  .and(billingConfigByTypeSchema)
  .and(
    z.object({
      members: z.array(addMemberSchema).optional(),
    }),
  )

/**
 * Update schema: all fields optional. billing_type + billing_config pair must
 * still be consistent when both are provided.
 */
export const updateProjectSchema = z
  .object({
    name: z.string().min(1).optional(),
    customer_id: z.string().uuid().nullable().optional(),
    description: z.string().nullable().optional(),
    status: projectStatusSchema.optional(),
    billing_type: z.enum(['fixed', 'hourly', 'retainer']).optional(),
    billing_config: z
      .union([
        fixedBillingConfigSchema,
        hourlyBillingConfigSchema,
        retainerBillingConfigSchema,
      ])
      .optional(),
    currency: z.string().min(1).max(10).optional(),
    start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
    end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
  })
  .partial()

/**
 * List/filter query params schema.
 * limit clamped to ≤100 on parse.
 */
export const listProjectsQuerySchema = z.object({
  cursor: z.string().optional(),
  limit: z
    .string()
    .optional()
    .transform((v) => (v ? Math.min(parseInt(v, 10) || 50, 100) : 50)),
  status: z
    .string()
    .optional()
    .transform((value) => {
      if (!value) return undefined
      const parts = value.split(',').map((part) => part.trim()).filter(Boolean)
      if (parts.length === 0) return undefined
      const parsed = z.array(projectStatusSchema).safeParse(parts)
      if (!parsed.success) {
        throw new Error('Invalid status filter')
      }
      return parsed.data.length === 1 ? parsed.data[0] : parsed.data
    }),
  billing_type: z.enum(['fixed', 'hourly', 'retainer']).optional(),
  customer_id: z.string().uuid().optional(),
  sort: z.enum(['name', 'start_date', 'updated_at']).optional(),
})

// ── Inferred types ────────────────────────────────────────────────────────────

export type CreateProjectInput = z.infer<typeof createProjectSchema>
export type UpdateProjectInput = z.infer<typeof updateProjectSchema>
export type AddMemberInput = z.infer<typeof addMemberSchema>
export type UpdateMemberInput = z.infer<typeof updateMemberSchema>
export type ListProjectsQueryInput = z.infer<typeof listProjectsQuerySchema>
