/**
 * Notification Validation Service
 * 
 * Provides runtime schema validation for notification inputs using Zod
 * Ensures data integrity and prevents invalid data from crossing the bridge
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

import { z } from 'zod';

/**
 * Schema for basic notification options
 */
export const NotificationOptionsSchema = z.object({
  time: z.string()
    .regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, 'Invalid time format. Use HH:MM')
    .refine((time) => {
      const [hours, minutes] = time.split(':').map(Number);
      return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59;
    }, 'Invalid time values. Hour must be 0-23, minute must be 0-59'),
  
  title: z.string()
    .min(1, 'Title is required')
    .max(100, 'Title must be less than 100 characters')
    .refine((title) => title.trim().length > 0, 'Title cannot be empty'),
  
  body: z.string()
    .min(1, 'Body is required')
    .max(500, 'Body must be less than 500 characters')
    .refine((body) => body.trim().length > 0, 'Body cannot be empty'),
  
  sound: z.boolean().optional().default(true),
  
  priority: z.enum(['low', 'default', 'high']).optional().default('default'),
  
  url: z.string()
    .url('Invalid URL format')
    .optional()
    .or(z.literal('')),
  
  channel: z.string()
    .min(1, 'Channel is required')
    .max(50, 'Channel must be less than 50 characters')
    .optional().default('daily-notifications'),
  
  timezone: z.string()
    .min(1, 'Timezone is required')
    .refine((tz) => {
      try {
        Intl.DateTimeFormat(undefined, { timeZone: tz });
        return true;
      } catch {
        return false;
      }
    }, 'Invalid timezone identifier')
    .optional().default('UTC')
});

/**
 * Schema for advanced reminder options
 */
export const ReminderOptionsSchema = z.object({
  id: z.string()
    .min(1, 'ID is required')
    .max(50, 'ID must be less than 50 characters')
    .regex(/^[a-zA-Z0-9_-]+$/, 'ID can only contain letters, numbers, underscores, and hyphens'),
  
  title: z.string()
    .min(1, 'Title is required')
    .max(100, 'Title must be less than 100 characters')
    .refine((title) => title.trim().length > 0, 'Title cannot be empty'),
  
  body: z.string()
    .min(1, 'Body is required')
    .max(500, 'Body must be less than 500 characters')
    .refine((body) => body.trim().length > 0, 'Body cannot be empty'),
  
  time: z.string()
    .regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, 'Invalid time format. Use HH:MM')
    .refine((time) => {
      const [hours, minutes] = time.split(':').map(Number);
      return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59;
    }, 'Invalid time values. Hour must be 0-23, minute must be 0-59'),
  
  sound: z.boolean().optional().default(true),
  
  vibration: z.boolean().optional().default(true),
  
  priority: z.enum(['low', 'normal', 'high']).optional().default('normal'),
  
  repeatDaily: z.boolean().optional().default(true),
  
  timezone: z.string()
    .min(1, 'Timezone is required')
    .refine((tz) => {
      try {
        Intl.DateTimeFormat(undefined, { timeZone: tz });
        return true;
      } catch {
        return false;
      }
    }, 'Invalid timezone identifier')
    .optional().default('UTC'),
  
  actions: z.array(z.object({
    id: z.string().min(1).max(20),
    title: z.string().min(1).max(30)
  })).optional().default([])
});

/**
 * Schema for content fetch configuration
 */
export const ContentFetchConfigSchema = z.object({
  schedule: z.string()
    .min(1, 'Schedule is required')
    .regex(/^(\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d)$/, 'Invalid cron format'),
  
  ttlSeconds: z.number()
    .int('TTL must be an integer')
    .min(60, 'TTL must be at least 60 seconds')
    .max(86400, 'TTL must be less than 24 hours'),
  
  source: z.string()
    .min(1, 'Source is required')
    .max(50, 'Source must be less than 50 characters'),
  
  url: z.string()
    .url('Invalid URL format')
    .min(1, 'URL is required'),
  
  headers: z.record(z.string()).optional().default({}),
  
  retryAttempts: z.number()
    .int('Retry attempts must be an integer')
    .min(0, 'Retry attempts cannot be negative')
    .max(10, 'Retry attempts cannot exceed 10')
    .optional().default(3),
  
  timeout: z.number()
    .int('Timeout must be an integer')
    .min(1000, 'Timeout must be at least 1000ms')
    .max(60000, 'Timeout must be less than 60 seconds')
    .optional().default(30000)
});

/**
 * Schema for user notification configuration
 */
export const UserNotificationConfigSchema = z.object({
  schedule: z.string()
    .min(1, 'Schedule is required')
    .refine((schedule) => {
      if (schedule === 'immediate') return true;
      return /^(\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d) (\*|[0-5]?\d)$/.test(schedule);
    }, 'Invalid schedule format. Use cron format or "immediate"'),
  
  title: z.string()
    .min(1, 'Title is required')
    .max(100, 'Title must be less than 100 characters'),
  
  body: z.string()
    .min(1, 'Body is required')
    .max(500, 'Body must be less than 500 characters'),
  
  actions: z.array(z.object({
    id: z.string().min(1).max(20),
    title: z.string().min(1).max(30)
  })).optional().default([]),
  
  sound: z.boolean().optional().default(true),
  
  vibration: z.boolean().optional().default(true),
  
  priority: z.enum(['low', 'normal', 'high']).optional().default('normal'),
  
  channel: z.string()
    .min(1, 'Channel is required')
    .max(50, 'Channel must be less than 50 characters')
    .optional().default('user-notifications')
});

/**
 * Schema for dual schedule configuration
 */
export const DualScheduleConfigurationSchema = z.object({
  contentFetch: ContentFetchConfigSchema,
  userNotification: UserNotificationConfigSchema,
  
  coordination: z.object({
    enabled: z.boolean().optional().default(true),
    maxDelayMinutes: z.number()
      .int('Max delay must be an integer')
      .min(0, 'Max delay cannot be negative')
      .max(60, 'Max delay cannot exceed 60 minutes')
      .optional().default(10)
  }).optional().default({})
});

/**
 * Validation result interface
 */
export interface ValidationResult<T> {
  success: boolean;
  data?: T;
  errors?: string[];
}

/**
 * Notification Validation Service
 */
export class NotificationValidationService {
  private static instance: NotificationValidationService;
  
  private constructor() {
    // Singleton constructor - no initialization needed
  }
  
  public static getInstance(): NotificationValidationService {
    if (!NotificationValidationService.instance) {
      NotificationValidationService.instance = new NotificationValidationService();
    }
    return NotificationValidationService.instance;
  }
  
  /**
   * Validate basic notification options
   */
  public validateNotificationOptions(options: unknown): ValidationResult<z.infer<typeof NotificationOptionsSchema>> {
    try {
      const validatedOptions = NotificationOptionsSchema.parse(options);
      return {
        success: true,
        data: validatedOptions
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate reminder options
   */
  public validateReminderOptions(options: unknown): ValidationResult<z.infer<typeof ReminderOptionsSchema>> {
    try {
      const validatedOptions = ReminderOptionsSchema.parse(options);
      return {
        success: true,
        data: validatedOptions
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate content fetch configuration
   */
  public validateContentFetchConfig(config: unknown): ValidationResult<z.infer<typeof ContentFetchConfigSchema>> {
    try {
      const validatedConfig = ContentFetchConfigSchema.parse(config);
      return {
        success: true,
        data: validatedConfig
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate user notification configuration
   */
  public validateUserNotificationConfig(config: unknown): ValidationResult<z.infer<typeof UserNotificationConfigSchema>> {
    try {
      const validatedConfig = UserNotificationConfigSchema.parse(config);
      return {
        success: true,
        data: validatedConfig
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate dual schedule configuration
   */
  public validateDualScheduleConfig(config: unknown): ValidationResult<z.infer<typeof DualScheduleConfigurationSchema>> {
    try {
      const validatedConfig = DualScheduleConfigurationSchema.parse(config);
      return {
        success: true,
        data: validatedConfig
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate time string format
   */
  public validateTimeString(time: string): ValidationResult<string> {
    try {
      const timeSchema = z.string()
        .regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, 'Invalid time format. Use HH:MM')
        .refine((time) => {
          const [hours, minutes] = time.split(':').map(Number);
          return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59;
        }, 'Invalid time values. Hour must be 0-23, minute must be 0-59');
      
      const validatedTime = timeSchema.parse(time);
      return {
        success: true,
        data: validatedTime
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => e.message)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Validate timezone string
   */
  public validateTimezone(timezone: string): ValidationResult<string> {
    try {
      const timezoneSchema = z.string()
        .min(1, 'Timezone is required')
        .refine((tz) => {
          try {
            Intl.DateTimeFormat(undefined, { timeZone: tz });
            return true;
          } catch {
            return false;
          }
        }, 'Invalid timezone identifier');
      
      const validatedTimezone = timezoneSchema.parse(timezone);
      return {
        success: true,
        data: validatedTimezone
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          success: false,
          errors: error.errors.map(e => e.message)
        };
      }
      return {
        success: false,
        errors: ['Unknown validation error']
      };
    }
  }
  
  /**
   * Get validation schema for a specific type
   */
  public getSchema(type: 'notification' | 'reminder' | 'contentFetch' | 'userNotification' | 'dualSchedule'): z.ZodType {
    switch (type) {
      case 'notification':
        return NotificationOptionsSchema;
      case 'reminder':
        return ReminderOptionsSchema;
      case 'contentFetch':
        return ContentFetchConfigSchema;
      case 'userNotification':
        return UserNotificationConfigSchema;
      case 'dualSchedule':
        return DualScheduleConfigurationSchema;
      default:
        throw new Error(`Unknown schema type: ${type}`);
    }
  }
}

/**
 * Enhanced DailyNotificationPlugin with validation
 */
export class ValidatedDailyNotificationPlugin {
  private validationService: NotificationValidationService;
  
  constructor() {
    this.validationService = NotificationValidationService.getInstance();
  }
  
  /**
   * Schedule daily notification with validation
   */
  async scheduleDailyNotification(options: unknown): Promise<void> {
    const validation = this.validationService.validateNotificationOptions(options);
    
    if (!validation.success) {
      throw new Error(`Validation failed: ${validation.errors?.join(', ')}`);
    }
    
    // Call native implementation with validated data
    if (!validation.data) {
      throw new Error('Validation passed but data is null');
    }
    return await this.nativeScheduleDailyNotification(validation.data);
  }
  
  /**
   * Schedule daily reminder with validation
   */
  async scheduleDailyReminder(options: unknown): Promise<void> {
    const validation = this.validationService.validateReminderOptions(options);
    
    if (!validation.success) {
      throw new Error(`Validation failed: ${validation.errors?.join(', ')}`);
    }
    
    // Call native implementation with validated data
    if (!validation.data) {
      throw new Error('Validation passed but data is null');
    }
    return await this.nativeScheduleDailyReminder(validation.data);
  }
  
  /**
   * Schedule content fetch with validation
   */
  async scheduleContentFetch(config: unknown): Promise<void> {
    const validation = this.validationService.validateContentFetchConfig(config);
    
    if (!validation.success) {
      throw new Error(`Validation failed: ${validation.errors?.join(', ')}`);
    }
    
    // Call native implementation with validated data
    if (!validation.data) {
      throw new Error('Validation passed but data is null');
    }
    return await this.nativeScheduleContentFetch(validation.data);
  }
  
  /**
   * Schedule user notification with validation
   */
  async scheduleUserNotification(config: unknown): Promise<void> {
    const validation = this.validationService.validateUserNotificationConfig(config);
    
    if (!validation.success) {
      throw new Error(`Validation failed: ${validation.errors?.join(', ')}`);
    }
    
    // Call native implementation with validated data
    if (!validation.data) {
      throw new Error('Validation passed but data is null');
    }
    return await this.nativeScheduleUserNotification(validation.data);
  }
  
  /**
   * Schedule dual notification with validation
   */
  async scheduleDualNotification(config: unknown): Promise<void> {
    const validation = this.validationService.validateDualScheduleConfig(config);
    
    if (!validation.success) {
      throw new Error(`Validation failed: ${validation.errors?.join(', ')}`);
    }
    
    // Call native implementation with validated data
    if (!validation.data) {
      throw new Error('Validation passed but data is null');
    }
    return await this.nativeScheduleDualNotification(validation.data);
  }
  
  // Native implementation methods (to be implemented)
  private async nativeScheduleDailyNotification(_options: z.infer<typeof NotificationOptionsSchema>): Promise<void> {
    // Implementation will call the actual plugin
    throw new Error('Native implementation not yet connected');
  }
  
  private async nativeScheduleDailyReminder(_options: z.infer<typeof ReminderOptionsSchema>): Promise<void> {
    // Implementation will call the actual plugin
    throw new Error('Native implementation not yet connected');
  }
  
  private async nativeScheduleContentFetch(_config: z.infer<typeof ContentFetchConfigSchema>): Promise<void> {
    // Implementation will call the actual plugin
    throw new Error('Native implementation not yet connected');
  }
  
  private async nativeScheduleUserNotification(_config: z.infer<typeof UserNotificationConfigSchema>): Promise<void> {
    // Implementation will call the actual plugin
    throw new Error('Native implementation not yet connected');
  }
  
  private async nativeScheduleDualNotification(_config: z.infer<typeof DualScheduleConfigurationSchema>): Promise<void> {
    // Implementation will call the actual plugin
    throw new Error('Native implementation not yet connected');
  }
}

export default NotificationValidationService;
