/**
 * TimeSafari Database Integration Service
 * 
 * Handles database integration with TimeSafari's storage patterns.
 * Supports SQLite (mobile/desktop) with
 * storage adapter pattern to avoid plugin owning DB lifecycle.
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

import { TimeSafariStorageAdapterImpl, StorageFactory } from '../timesafari-storage-adapter';
import { observability, EVENT_CODES } from '../observability';

/**
 * Database Integration Configuration
 */
export interface DatabaseIntegrationConfig {
  /**
   * Platform type
   */
  platform: 'android' | 'ios' | 'electron';
  
  /**
   * Storage adapter from host
   */
  storageAdapter?: TimeSafariStorageAdapterImpl;
  
  /**
   * Database configuration
   */
  database?: DatabaseConfig;
  
  /**
   * Watermark management configuration
   */
  watermark?: WatermarkConfig;
}

/**
 * Database Configuration
 */
export interface DatabaseConfig {
  /**
   * Database name
   */
  name: string;
  
  /**
   * Database version
   */
  version: number;
  
  /**
   * Table schemas
   */
  tables: TableSchema[];
  
  /**
   * Migration scripts
   */
  migrations: MigrationScript[];
}

/**
 * Table Schema
 */
export interface TableSchema {
  name: string;
  columns: ColumnDefinition[];
  indexes: IndexDefinition[];
  constraints: ConstraintDefinition[];
}

/**
 * Column Definition
 */
export interface ColumnDefinition {
  name: string;
  type: 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'BOOLEAN';
  nullable: boolean;
  primaryKey: boolean;
  autoIncrement: boolean;
  defaultValue?: unknown;
}

/**
 * Index Definition
 */
export interface IndexDefinition {
  name: string;
  columns: string[];
  unique: boolean;
}

/**
 * Constraint Definition
 */
export interface ConstraintDefinition {
  name: string;
  type: 'FOREIGN_KEY' | 'CHECK' | 'UNIQUE';
  definition: string;
}

/**
 * Migration Script
 */
export interface MigrationScript {
  version: number;
  up: string;
  down: string;
}

/**
 * Watermark Configuration
 */
export interface WatermarkConfig {
  /**
   * Enable watermark management
   */
  enabled: boolean;
  
  /**
   * Watermark table name
   */
  tableName: string;
  
  /**
   * Watermark column name
   */
  columnName: string;
  
  /**
   * Initial watermark value
   */
  initialValue: number;
  
  /**
   * Watermark update interval (milliseconds)
   */
  updateInterval: number;
}

/**
 * Database Integration Service
 */
export class DatabaseIntegrationService {
  private static instance: DatabaseIntegrationService;
  private config: DatabaseIntegrationConfig;
  private storageAdapter: TimeSafariStorageAdapterImpl;
  private initialized = false;
  private watermarkValue = 0;
  private watermarkUpdateTimer: NodeJS.Timeout | null = null;
  
  private constructor() {
    this.config = this.getDefaultConfig();
    this.storageAdapter = new TimeSafariStorageAdapterImpl(
      StorageFactory.createStorage('android')
    );
  }
  
  /**
   * Get singleton instance
   */
  static getInstance(): DatabaseIntegrationService {
    if (!DatabaseIntegrationService.instance) {
      DatabaseIntegrationService.instance = new DatabaseIntegrationService();
    }
    return DatabaseIntegrationService.instance;
  }
  
  /**
   * Initialize database integration
   */
  async initialize(config: DatabaseIntegrationConfig): Promise<void> {
    try {
      const eventId = this.generateEventId();
      observability.logEvent('INFO', EVENT_CODES.FETCH_START, 'Initializing database integration', { eventId });
      
      this.config = { ...this.getDefaultConfig(), ...config };
      
      // Use provided storage adapter or create default
      if (config.storageAdapter) {
        this.storageAdapter = config.storageAdapter;
      } else {
        this.storageAdapter = new TimeSafariStorageAdapterImpl(
          StorageFactory.createStorage(config.platform)
        );
      }
      
      // Initialize database schema
      await this.initializeDatabaseSchema();
      
      // Initialize watermark management
      if (this.config.watermark?.enabled) {
        await this.initializeWatermarkManagement();
      }
      
      this.initialized = true;
      
      observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Database integration initialized successfully', { 
        eventId,
        platform: config.platform,
        databaseName: this.config.database?.name
      });
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to initialize database integration', { 
        eventId,
        error: (error as Error).message
      });
      
      throw error;
    }
  }
  
  /**
   * Store notification data
   */
  async storeNotificationData(data: {
    id: string;
    title: string;
    body: string;
    scheduledTime: number;
    channel: string;
    activeDid: string;
    metadata?: unknown;
  }): Promise<void> {
    this.checkInitialized();
    
    try {
      const eventId = this.generateEventId();
      
      // Store in TimeSafari storage adapter
      await this.storageAdapter.store(`notification_${data.id}`, {
        ...data,
        storedAt: Date.now(),
        watermark: this.watermarkValue
      }, 7 * 24 * 60 * 60 * 1000); // 7 days TTL
      
      observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Notification data stored successfully', { 
        eventId,
        notificationId: data.id,
        channel: data.channel
      });
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to store notification data', { 
        eventId,
        error: (error as Error).message,
        notificationId: data.id
      });
      
      throw error;
    }
  }
  
  /**
   * Retrieve notification data
   */
  async retrieveNotificationData(notificationId: string): Promise<unknown> {
    this.checkInitialized();
    
    try {
      const data = await this.storageAdapter.retrieve(`notification_${notificationId}`);
      
      if (data) {
        const eventId = this.generateEventId();
        observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Notification data retrieved successfully', { 
          eventId,
          notificationId
        });
      }
      
      return data;
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to retrieve notification data', { 
        eventId,
        error: (error as Error).message,
        notificationId
      });
      
      throw error;
    }
  }
  
  /**
   * Update watermark value
   */
  async updateWatermark(newValue: number): Promise<void> {
    this.checkInitialized();
    
    if (!this.config.watermark?.enabled) {
      return;
    }
    
    try {
      const eventId = this.generateEventId();
      const oldValue = this.watermarkValue;
      
      this.watermarkValue = newValue;
      
      // Store watermark in storage adapter
      await this.storageAdapter.store('watermark', {
        value: this.watermarkValue,
        updatedAt: Date.now()
      });
      
      observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Watermark updated successfully', { 
        eventId,
        oldValue,
        newValue: this.watermarkValue
      });
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to update watermark', { 
        eventId,
        error: (error as Error).message,
        newValue
      });
      
      throw error;
    }
  }
  
  /**
   * Get current watermark value
   */
  async getWatermark(): Promise<number> {
    this.checkInitialized();
    
    if (!this.config.watermark?.enabled) {
      return 0;
    }
    
    try {
      const watermarkData = await this.storageAdapter.retrieve('watermark');
      if (watermarkData && (watermarkData as { value: number }).value) {
        this.watermarkValue = (watermarkData as { value: number }).value;
      }
      
      return this.watermarkValue;
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to get watermark', { 
        eventId,
        error: (error as Error).message
      });
      
      return 0;
    }
  }
  
  /**
   * Get database statistics
   */
  async getDatabaseStats(): Promise<{
    totalNotifications: number;
    totalStorageSize: number;
    watermarkValue: number;
    lastUpdated: number;
  }> {
    this.checkInitialized();
    
    try {
      const stats = await this.storageAdapter.getStats();
      const watermark = await this.getWatermark();
      
      return {
        totalNotifications: stats.validKeys,
        totalStorageSize: stats.totalSizeBytes,
        watermarkValue: watermark,
        lastUpdated: Date.now()
      };
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to get database stats', { 
        eventId,
        error: (error as Error).message
      });
      
      throw error;
    }
  }
  
  /**
   * Cleanup expired data
   */
  async cleanupExpiredData(): Promise<void> {
    this.checkInitialized();
    
    try {
      const eventId = this.generateEventId();
      
      await this.storageAdapter.cleanupExpired();
      
      observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Expired data cleaned up successfully', { eventId });
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to cleanup expired data', { 
        eventId,
        error: (error as Error).message
      });
      
      throw error;
    }
  }
  
  /**
   * Shutdown database integration
   */
  async shutdown(): Promise<void> {
    try {
      const eventId = this.generateEventId();
      observability.logEvent('INFO', EVENT_CODES.FETCH_START, 'Shutting down database integration', { eventId });
      
      // Clear watermark update timer
      if (this.watermarkUpdateTimer) {
        clearInterval(this.watermarkUpdateTimer);
        this.watermarkUpdateTimer = null;
      }
      
      this.initialized = false;
      
      observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Database integration shutdown complete', { eventId });
      
    } catch (error) {
      const eventId = this.generateEventId();
      observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Error during database integration shutdown', { 
        eventId,
        error: (error as Error).message
      });
      
      throw error;
    }
  }
  
  // Private methods
  
  private getDefaultConfig(): DatabaseIntegrationConfig {
    return {
      platform: 'android',
      database: {
        name: 'timesafari_daily_notifications',
        version: 1,
        tables: [
          {
            name: 'notifications',
            columns: [
              { name: 'id', type: 'TEXT', nullable: false, primaryKey: true, autoIncrement: false },
              { name: 'title', type: 'TEXT', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'body', type: 'TEXT', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'scheduled_time', type: 'INTEGER', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'channel', type: 'TEXT', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'active_did', type: 'TEXT', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'metadata', type: 'TEXT', nullable: true, primaryKey: false, autoIncrement: false },
              { name: 'created_at', type: 'INTEGER', nullable: false, primaryKey: false, autoIncrement: false },
              { name: 'updated_at', type: 'INTEGER', nullable: false, primaryKey: false, autoIncrement: false }
            ],
            indexes: [
              { name: 'idx_notifications_scheduled_time', columns: ['scheduled_time'], unique: false },
              { name: 'idx_notifications_channel', columns: ['channel'], unique: false },
              { name: 'idx_notifications_active_did', columns: ['active_did'], unique: false }
            ],
            constraints: []
          }
        ],
        migrations: []
      },
      watermark: {
        enabled: true,
        tableName: 'watermarks',
        columnName: 'value',
        initialValue: 0,
        updateInterval: 60000 // 1 minute
      }
    };
  }
  
  private checkInitialized(): void {
    if (!this.initialized) {
      throw new Error('DatabaseIntegrationService not initialized. Call initialize() first.');
    }
  }
  
  private async initializeDatabaseSchema(): Promise<void> {
    // In a real implementation, this would create the database schema
    // For now, we'll just store the schema metadata
    await this.storageAdapter.store('database_schema', {
      version: this.config.database?.version || 1,
      tables: this.config.database?.tables || [],
      initializedAt: Date.now()
    });
  }
  
  private async initializeWatermarkManagement(): Promise<void> {
    if (!this.config.watermark?.enabled) {
      return;
    }
    
    // Get current watermark value (skip initialization check for initial setup)
    try {
      const watermarkData = await this.storageAdapter.retrieve('watermark');
      if (watermarkData && (watermarkData as { value: number }).value) {
        this.watermarkValue = (watermarkData as { value: number }).value;
      }
    } catch (error) {
      // Use default value if retrieval fails
      this.watermarkValue = this.config.watermark.initialValue;
    }
    
    // Set up periodic watermark updates
    this.watermarkUpdateTimer = setInterval(async () => {
      try {
        const newValue = Date.now();
        await this.updateWatermark(newValue);
      } catch (error) {
        const eventId = this.generateEventId();
        observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to update watermark in timer', { 
          eventId,
          error: (error as Error).message
        });
      }
    }, this.config.watermark.updateInterval);
  }
  
  private generateEventId(): string {
    return `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}
