import { z } from 'zod';

export const ChannelEnum = z.enum(['email', 'sms', 'whatsapp']);

export const ApplicationQuotaSchema = z.object({
  per_min: z.number().int().positive({ message: 'per_min quota must be > 0' }),
  per_day: z.number().int().positive({ message: 'per_day quota must be > 0' }),
});

export const ApplicationSchema = z.object({
  id: z
    .string()
    .min(1)
    .regex(/^[a-z0-9_-]+$/, {
      message: 'Application ID must contain only lowercase alphanumeric characters, underscores, and hyphens',
    }),
  name: z.string().min(1),
  enabled: z.boolean().default(true),
  source: z.enum(['manual']).default('manual'),
  // Empty is allowed for draft apps; dispatch still fails closed when a channel is not listed.
  channels: z.array(ChannelEnum).default([]),
  quota: ApplicationQuotaSchema,
  bindings: z
    .record(ChannelEnum, z.array(z.string()))
    .default({}),
  allowed_ips: z.array(z.string().min(1)).optional(),
});

export const ApplicationsFileSchema = z.object({
  applications: z.array(ApplicationSchema),
});

export const ProviderAdapterEnum = z.string().min(1);

export const ProviderSchema = z.object({
    id: z
    .string()
    .min(1)
    .regex(/^[a-z0-9_-]+$/, {
      message: 'Provider ID must contain only lowercase alphanumeric characters, underscores, and hyphens',
    }),
  channel: ChannelEnum,
  adapter: ProviderAdapterEnum,
  /** Human-readable display name shown in the console (id stays stable for routing). */
  name: z.string().min(1),
  enabled: z.boolean().default(true),
  latency_ms: z.number().nonnegative().default(100),
  failure_rate: z.number().min(0).max(1).default(0),
  max_rps: z.number().positive().optional(),
  cost_per_message: z.number().nonnegative().optional(),
  config: z.record(z.string(), z.unknown()).default({}),
});

export const ProvidersFileSchema = z.object({
  providers: z.array(ProviderSchema),
});

export const RetryPolicySchema = z.object({
  max_attempts: z.number().int().min(1).max(5).default(2),
  backoff_ms: z.number().int().nonnegative().default(200),
});

export const TimeoutConfigSchema = z.object({
  email: z.number().int().min(100).max(60000).default(10000),
  sms: z.number().int().min(100).max(60000).default(10000),
  whatsapp: z.number().int().min(100).max(60000).default(15000),
});

export const CircuitBreakerConfigSchema = z.object({
  failure_threshold: z.number().int().min(1).default(3),
  recovery_time_seconds: z.number().int().min(1).default(20),
});

export const ChannelStrategyEnum = z.enum(['failover', 'round-robin']);

export const RoutingConfigSchema = z.object({
  // Empty until operators connect providers; dispatch fails closed with no candidates.
  default: z.object({
    email: z.array(z.string()).default([]),
    sms: z.array(z.string()).default([]),
    whatsapp: z.array(z.string()).default([]),
  }),
  strategies: z
    .object({
      email: ChannelStrategyEnum.default('failover'),
      sms: ChannelStrategyEnum.default('failover'),
      whatsapp: ChannelStrategyEnum.default('failover'),
    })
    .default({ email: 'failover', sms: 'failover', whatsapp: 'failover' }),
  retry: RetryPolicySchema.default({ max_attempts: 2, backoff_ms: 200 }),
  timeout_ms: TimeoutConfigSchema.default({ email: 10000, sms: 10000, whatsapp: 15000 }),
  circuit_breaker: CircuitBreakerConfigSchema.default({ failure_threshold: 3, recovery_time_seconds: 20 }),
});

export const RoutingFileSchema = z.object({
  routing: RoutingConfigSchema,
});

export const ZyphonServerSchema = z.object({
  host: z.string().default('0.0.0.0'),
  public_port: z.number().int().positive().default(8080),
  admin_port: z.number().int().positive().default(8081),
  smtp_relay: z
    .object({
      enabled: z.boolean().default(false),
      port: z.number().int().positive().default(2525),
      host: z.string().default('0.0.0.0'),
    })
    .default({ enabled: false, port: 2525, host: '0.0.0.0' }),
  data_dir: z.string().default('./data'),
  config_dir: z.string().default('./config'),
  retention_days: z.number().int().positive().default(7),
  ring_buffer_size: z.number().int().positive().default(1000),
  store_bodies: z.boolean().default(false),
});

export const ZyphonFileSchema = z.object({
  server: ZyphonServerSchema.default({}),
});

export const PoliciesSchema = z.object({
  max_payload_kb: z.number().int().positive().default(256),
  request_timeout_ms: z.number().int().positive().default(15000),
  queue_size: z.number().int().positive().default(500),
  concurrency: z.number().int().positive().default(10),
});

export const PoliciesFileSchema = z.object({
  policies: PoliciesSchema.default({}),
});

export const DltHeaderSchema = z.object({
  id: z.string().min(1),
  header: z.string().min(1),
  category: z.enum(['transactional', 'promotional', 'otp', 'service']).default('transactional'),
});

export const DltConfigSchema = z.object({
  pe_id: z
    .string()
    .regex(/^\d{19}$/, { message: 'Principal Entity ID must be exactly 19 digits' })
    .optional(),
  entity_name: z.string().optional(),
  headers: z.array(DltHeaderSchema).default([]),
  quiet_hours: z
    .object({
      enabled: z.boolean().default(true),
      start: z.string().regex(/^\d{2}:\d{2}$/).default('21:00'),
      end: z.string().regex(/^\d{2}:\d{2}$/).default('09:00'),
      timezone: z.literal('Asia/Kolkata').default('Asia/Kolkata'),
    })
    .default({ enabled: true, start: '21:00', end: '09:00', timezone: 'Asia/Kolkata' }),
  default_country_code: z.string().regex(/^\d{1,3}$/).default('91'),
});

export const DltFileSchema = z.object({
  dlt: DltConfigSchema.default({}),
});

export const MessageTemplateSchema = z.object({
  id: z.string().min(1),
  name: z.string().min(1),
  channel: ChannelEnum,
  subject: z.string().optional(),
  body: z.string().min(1),
  dlt_id: z.string().optional(),
  header: z.string().optional(),
  category: z.enum(['transactional', 'promotional', 'otp', 'service']).optional(),
  variables: z.array(z.string()).default([]),
});

export const TemplatesFileSchema = z.object({
  templates: z.array(MessageTemplateSchema).default([]),
});

export const SuppressionsFileSchema = z.object({
  suppressions: z
    .object({
      sms: z.array(z.string()).default([]),
      email: z.array(z.string()).default([]),
      whatsapp: z.array(z.string()).default([]),
    })
    .default({ sms: [], email: [], whatsapp: [] }),
});

export const AlertsConfigSchema = z.object({
  email: z.string().email().optional().or(z.literal('')),
  provider_down: z.boolean().default(true),
  quota_warning: z.boolean().default(true),
  high_failure_rate: z.boolean().default(false),
});

export const AlertsFileSchema = z.object({
  alerts: AlertsConfigSchema.default({}),
});

export const TeamMemberSchema = z.object({
  email: z.string().email(),
  role: z.enum(['admin', 'operator', 'viewer']).default('viewer'),
});

export const TeamFileSchema = z.object({
  team: z.array(TeamMemberSchema).default([]),
});

export type ApplicationConfigModel = z.infer<typeof ApplicationSchema>;
export type ApplicationsFileModel = z.infer<typeof ApplicationsFileSchema>;
export type ProviderConfigModel = z.infer<typeof ProviderSchema>;
export type ProvidersFileModel = z.infer<typeof ProvidersFileSchema>;
export type RoutingConfigModel = z.infer<typeof RoutingConfigSchema>;
export type RoutingFileModel = z.infer<typeof RoutingFileSchema>;
export type ZyphonServerConfigModel = z.infer<typeof ZyphonServerSchema>;
export type ZyphonFileModel = z.infer<typeof ZyphonFileSchema>;
export type PoliciesConfigModel = z.infer<typeof PoliciesSchema>;
export type PoliciesFileModel = z.infer<typeof PoliciesFileSchema>;
export type DltConfigModel = z.infer<typeof DltConfigSchema>;
export type DltFileModel = z.infer<typeof DltFileSchema>;
export type MessageTemplateModel = z.infer<typeof MessageTemplateSchema>;
export type TemplatesFileModel = z.infer<typeof TemplatesFileSchema>;
export type SuppressionsFileModel = z.infer<typeof SuppressionsFileSchema>;
export type AlertsFileModel = z.infer<typeof AlertsFileSchema>;
export type TeamFileModel = z.infer<typeof TeamFileSchema>;

