import { ref, computed } from 'vue'
import { BaseService } from '@/core/base/BaseService'

export interface VisibilityState {
  isVisible: boolean
  isOnline: boolean
  lastHiddenAt: number | null
  lastVisibleAt: number | null
  hiddenDuration: number | null
}

export interface VisibilityOptions {
  reconnectThreshold?: number // Time in ms before reconnection is needed (default: 30s)
  debounceDelay?: number // Debounce rapid visibility changes (default: 100ms)
  checkInterval?: number // Interval to check connection health (default: 5s when visible)
}

/**
 * Centralized Visibility Service
 * Manages app visibility state and coordinates connection recovery
 */
export class VisibilityService extends BaseService {
  // Service metadata
  protected readonly metadata = {
    name: 'VisibilityService',
    version: '1.0.0',
    dependencies: []
  }

  // Configuration
  private readonly reconnectThreshold = 30000 // 30 seconds
  private readonly debounceDelay = 100 // 100ms
  private readonly checkInterval = 5000 // 5 seconds

  // State
  private _isVisible = ref(true)
  private _isOnline = ref(true)
  private _lastHiddenAt = ref<number | null>(null)
  private _lastVisibleAt = ref<number | null>(null)
  private _isPaused = ref(false)

  // Listeners
  private visibilityHandler?: () => void
  private onlineHandler?: () => void
  private offlineHandler?: () => void
  private focusHandler?: () => void
  private blurHandler?: () => void
  private debounceTimer?: number
  private healthCheckInterval?: number

  // Subscribed services that need reconnection
  private subscribedServices = new Set<{
    name: string
    onResume: () => Promise<void>
    onPause: () => Promise<void>
  }>()

  // Public reactive state
  public readonly isVisible = computed(() => this._isVisible.value)
  public readonly isOnline = computed(() => this._isOnline.value)
  public readonly isPaused = computed(() => this._isPaused.value)
  public readonly lastHiddenAt = computed(() => this._lastHiddenAt.value)
  public readonly lastVisibleAt = computed(() => this._lastVisibleAt.value)
  
  public readonly hiddenDuration = computed(() => {
    if (!this._lastHiddenAt.value) return null
    if (this._isVisible.value) {
      return this._lastVisibleAt.value 
        ? this._lastVisibleAt.value - this._lastHiddenAt.value
        : null
    }
    return Date.now() - this._lastHiddenAt.value
  })

  public readonly needsReconnection = computed(() => {
    const duration = this.hiddenDuration.value
    return duration !== null && duration > this.reconnectThreshold
  })

  /**
   * Service-specific initialization (called by BaseService)
   */
  protected async onInitialize(): Promise<void> {
    this.setupVisibilityHandlers()
    this.setupNetworkHandlers()
    this.setupFocusHandlers()
    this.startHealthCheck()
    
    // Set initial state
    this._isVisible.value = !document?.hidden
    this._isOnline.value = navigator?.onLine ?? true
    
    this.debug('VisibilityService initialized', {
      isVisible: this._isVisible.value,
      isOnline: this._isOnline.value
    })
  }

  /**
   * Register a service for visibility-based lifecycle management
   */
  registerService(
    name: string,
    onResume: () => Promise<void>,
    onPause: () => Promise<void>
  ): () => void {
    const service = { name, onResume, onPause }
    this.subscribedServices.add(service)
    
    this.debug(`Service registered: ${name}`)
    
    // Return unregister function
    return () => {
      this.subscribedServices.delete(service)
      this.debug(`Service unregistered: ${name}`)
    }
  }

  /**
   * Setup document visibility handlers
   */
  private setupVisibilityHandlers(): void {
    if (typeof document === 'undefined') return

    this.visibilityHandler = () => {
      // Debounce rapid changes
      if (this.debounceTimer) {
        clearTimeout(this.debounceTimer)
      }

      this.debounceTimer = setTimeout(() => {
        const wasVisible = this._isVisible.value
        const isNowVisible = !document.hidden

        if (wasVisible === isNowVisible) return

        this._isVisible.value = isNowVisible

        if (isNowVisible) {
          this.handleAppVisible()
        } else {
          this.handleAppHidden()
        }
      }, this.debounceDelay) as unknown as number
    }

    document.addEventListener('visibilitychange', this.visibilityHandler)
    
    // Also listen for page show/hide events (iOS Safari)
    window.addEventListener('pageshow', () => this.handleAppVisible())
    window.addEventListener('pagehide', () => this.handleAppHidden())
  }

  /**
   * Setup network status handlers
   */
  private setupNetworkHandlers(): void {
    if (typeof window === 'undefined') return

    this.onlineHandler = () => {
      this._isOnline.value = true
      this.debug('Network online')
      this.handleNetworkReconnect()
    }

    this.offlineHandler = () => {
      this._isOnline.value = false
      this.debug('Network offline')
      this.handleNetworkDisconnect()
    }

    window.addEventListener('online', this.onlineHandler)
    window.addEventListener('offline', this.offlineHandler)
  }

  /**
   * Setup window focus handlers (desktop)
   */
  private setupFocusHandlers(): void {
    if (typeof window === 'undefined') return

    // Window focus/blur for desktop browsers
    this.focusHandler = () => {
      if (!this._isVisible.value) {
        this._isVisible.value = true
        this.handleAppVisible()
      }
    }

    this.blurHandler = () => {
      // Don't treat blur as hidden on desktop unless document is actually hidden
      if (document.hidden && this._isVisible.value) {
        this._isVisible.value = false
        this.handleAppHidden()
      }
    }

    window.addEventListener('focus', this.focusHandler)
    window.addEventListener('blur', this.blurHandler)
  }

  /**
   * Handle app becoming visible
   */
  private async handleAppVisible(): Promise<void> {
    const now = Date.now()
    this._lastVisibleAt.value = now
    this._isPaused.value = false

    const hiddenDuration = this._lastHiddenAt.value 
      ? now - this._lastHiddenAt.value 
      : 0

    this.debug('App became visible', {
      hiddenDuration,
      needsReconnection: hiddenDuration > this.reconnectThreshold
    })

    // Resume services if we were hidden long enough
    if (hiddenDuration > this.reconnectThreshold) {
      await this.resumeAllServices()
    }

    // Restart health check
    this.startHealthCheck()
  }

  /**
   * Handle app becoming hidden
   */
  private async handleAppHidden(): Promise<void> {
    this._lastHiddenAt.value = Date.now()
    this._isPaused.value = true

    this.debug('App became hidden')

    // Stop health check while hidden
    this.stopHealthCheck()

    // Pause services after a delay (in case of quick tab switches)
    setTimeout(() => {
      if (this._isPaused.value) {
        this.pauseAllServices()
      }
    }, 5000) // 5 second delay before pausing
  }

  /**
   * Handle network reconnection
   */
  private async handleNetworkReconnect(): Promise<void> {
    if (!this._isVisible.value) return

    this.debug('Network reconnected, resuming services')
    await this.resumeAllServices()
  }

  /**
   * Handle network disconnection
   */
  private async handleNetworkDisconnect(): Promise<void> {
    this.debug('Network disconnected, pausing services')
    await this.pauseAllServices()
  }

  /**
   * Resume all registered services
   */
  private async resumeAllServices(): Promise<void> {
    this.debug(`Resuming ${this.subscribedServices.size} services`)

    const promises = Array.from(this.subscribedServices).map(async service => {
      try {
        await service.onResume()
        this.debug(`Resumed service: ${service.name}`)
      } catch (error) {
        this.handleError(error, `resumeService:${service.name}`)
      }
    })

    await Promise.allSettled(promises)
  }

  /**
   * Pause all registered services
   */
  private async pauseAllServices(): Promise<void> {
    this.debug(`Pausing ${this.subscribedServices.size} services`)

    const promises = Array.from(this.subscribedServices).map(async service => {
      try {
        await service.onPause()
        this.debug(`Paused service: ${service.name}`)
      } catch (error) {
        this.handleError(error, `pauseService:${service.name}`)
      }
    })

    await Promise.allSettled(promises)
  }

  /**
   * Start health check interval
   */
  private startHealthCheck(): void {
    this.stopHealthCheck()

    if (!this._isVisible.value) return

    this.healthCheckInterval = setInterval(() => {
      if (this._isVisible.value && this._isOnline.value) {
        // Emit health check event for services to respond to
        this.debug('Health check tick')
        // Services can listen to this to verify their connections
      }
    }, this.checkInterval) as unknown as number
  }

  /**
   * Stop health check interval
   */
  private stopHealthCheck(): void {
    if (this.healthCheckInterval) {
      clearInterval(this.healthCheckInterval)
      this.healthCheckInterval = undefined
    }
  }

  /**
   * Force check all connections
   */
  async forceConnectionCheck(): Promise<void> {
    this.debug('Forcing connection check')
    await this.resumeAllServices()
  }

  /**
   * Get current visibility state
   */
  getState(): VisibilityState {
    return {
      isVisible: this._isVisible.value,
      isOnline: this._isOnline.value,
      lastHiddenAt: this._lastHiddenAt.value,
      lastVisibleAt: this._lastVisibleAt.value,
      hiddenDuration: this.hiddenDuration.value
    }
  }

  /**
   * Cleanup when service is disposed (called by BaseService)
   */
  protected async onDispose(): Promise<void> {
    // Remove all event listeners
    if (typeof document !== 'undefined' && this.visibilityHandler) {
      document.removeEventListener('visibilitychange', this.visibilityHandler)
    }

    if (typeof window !== 'undefined') {
      if (this.onlineHandler) {
        window.removeEventListener('online', this.onlineHandler)
      }
      if (this.offlineHandler) {
        window.removeEventListener('offline', this.offlineHandler)
      }
      if (this.focusHandler) {
        window.removeEventListener('focus', this.focusHandler)
      }
      if (this.blurHandler) {
        window.removeEventListener('blur', this.blurHandler)
      }
      window.removeEventListener('pageshow', () => this.handleAppVisible())
      window.removeEventListener('pagehide', () => this.handleAppHidden())
    }

    // Clear timers
    if (this.debounceTimer) {
      clearTimeout(this.debounceTimer)
    }
    this.stopHealthCheck()

    // Clear subscribed services
    this.subscribedServices.clear()

    this.debug('VisibilityService disposed')
  }
}

// Export singleton instance
export const visibilityService = new VisibilityService()