// Main application store using Pinia

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { DataModel } from '../types/api'
import { wsClient, applyDataPatch } from '../api/websocket'
import { rpcClient } from '../api/rpc-client'

export const useAppStore = defineStore('app', () => {
  // State
  const data = ref<DataModel | null>(null)
  const isAuthenticated = ref(localStorage.getItem('neode-auth') === 'true')
  const isConnected = ref(false)
  const isReconnecting = ref(false)
  const isLoading = ref(false)
  const error = ref<string | null>(null)
  let isWsSubscribed = false
  let sessionValidated = false

  // Computed
  const serverInfo = computed(() => data.value?.['server-info'])
  const packages = computed(() => data.value?.['package-data'] || {})
  const uiData = computed(() => data.value?.ui)
  const serverName = computed(() => serverInfo.value?.name || 'Archipelago')
  const isRestarting = computed(() => serverInfo.value?.['status-info']?.restarting || false)
  const isShuttingDown = computed(() => serverInfo.value?.['status-info']?.['shutting-down'] || false)
  const isOffline = computed(() => !isConnected.value || isRestarting.value || isShuttingDown.value)

  // Actions
  async function login(password: string): Promise<{ requires_totp?: boolean }> {
    isLoading.value = true
    error.value = null

    try {
      const result = await rpcClient.login(password)
      if (result && result.requires_totp) {
        return { requires_totp: true }
      }

      isAuthenticated.value = true
      sessionValidated = true
      localStorage.setItem('neode-auth', 'true')

      // Initialize data structure immediately so dashboard can render
      await initializeData()

      // Connect WebSocket in background - don't block login flow
      connectWebSocket().catch((err) => {
        console.warn('[Store] WebSocket connection failed after login, will retry:', err)
      })
      return {}
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Login failed'
      throw err
    } finally {
      isLoading.value = false
    }
  }

  async function completeLoginAfterTotp(): Promise<void> {
    isAuthenticated.value = true
    sessionValidated = true
    localStorage.setItem('neode-auth', 'true')
    await initializeData()
    connectWebSocket().catch((err) => {
      console.warn('[Store] WebSocket connection failed after TOTP login, will retry:', err)
    })
  }

  async function logout(): Promise<void> {
    try {
      await rpcClient.logout()
    } catch (err) {
      console.error('Logout error:', err)
    } finally {
      isAuthenticated.value = false
      sessionValidated = false
      localStorage.removeItem('neode-auth')
      data.value = null
      isWsSubscribed = false
      wsClient.disconnect()
      isConnected.value = false
      isReconnecting.value = false
    }
  }

  async function connectWebSocket(): Promise<void> {
    try {
      console.log('[Store] Connecting WebSocket...')
      isReconnecting.value = true
      
      // Don't create multiple subscriptions - check if already subscribed
      if (!isWsSubscribed) {
        // Subscribe to updates BEFORE connecting (so we catch initial data)
        isWsSubscribed = true
        
        // Listen for connection state changes
        wsClient.onConnectionStateChange((connected) => {
          console.log('[Store] WebSocket connection state changed:', connected)
          isConnected.value = connected
          if (!connected) {
            isReconnecting.value = true
          } else {
            isReconnecting.value = false
          }
        })
        
        wsClient.subscribe((update: { type?: string; data?: DataModel; rev?: number; patch?: import('@/types/api').PatchOperation[] }) => {
        // Handle mock backend format: {type: 'initial', data: {...}}
        if (update?.type === 'initial' && update?.data) {
          console.log('[Store] Received initial data from mock backend')
          data.value = update.data
          isConnected.value = true
          isReconnecting.value = false
        }
        // Handle real backend format: {rev: 0, data: {...}}
        else if (update?.data && update?.rev !== undefined) {
          console.log('[Store] Received dump from real backend at revision', update.rev)
          data.value = update.data
          isConnected.value = true
          isReconnecting.value = false
        }
        // Handle patch updates (both backends)
        else if (data.value && update?.patch) {
          try {
            console.log('[Store] Applying patch at revision', update.rev || 'unknown')
          data.value = applyDataPatch(data.value, update.patch)
            // Mark as connected once we receive any valid patch
            if (!isConnected.value) {
              isConnected.value = true
              isReconnecting.value = false
            }
          } catch (err) {
            console.error('[Store] Failed to apply WebSocket patch:', err)
          }
        }
        })
      }
      
      // Now connect (or reconnect if already connected)
      // Only attempt to connect if not already connected
      if (wsClient.isConnected()) {
        console.log('[Store] WebSocket already connected')
        isConnected.value = true
        isReconnecting.value = false
        return
      }
      
      await wsClient.connect()
      console.log('[Store] WebSocket connected')
      
      // Connection state will be updated via the callback
      if (wsClient.isConnected()) {
        isConnected.value = true
        isReconnecting.value = false
      }
      
    } catch (err) {
      console.error('[Store] WebSocket connection failed:', err)
      // Don't mark as disconnected immediately - let reconnection logic handle it
      // The WebSocket client will retry automatically
      isReconnecting.value = true
      isConnected.value = false
      // Don't throw - allow app to work without real-time updates
      // The WebSocket will reconnect in the background
    }
  }

  async function initializeData(): Promise<void> {
    // Initialize with empty data structure
    // The WebSocket will populate it with real data
    data.value = {
      'server-info': {
        id: '',
        version: '',
        name: null,
        pubkey: '',
        'status-info': {
          restarting: false,
          'shutting-down': false,
          updated: false,
          'backup-progress': null,
          'update-progress': null,
        },
        'lan-address': null,
        'tor-address': null,
        unread: 0,
        'wifi-ssids': [],
        'zram-enabled': false,
      },
      'package-data': {},
      ui: {
        name: null,
        'ack-welcome': '',
        marketplace: {
          'selected-hosts': [],
          'known-hosts': {},
        },
        theme: 'dark',
      },
    }
  }
  
  // Check session validity on app load or stale auth
  async function checkSession(): Promise<boolean> {
    if (!localStorage.getItem('neode-auth')) {
      return false
    }
    
    try {
      await rpcClient.call({ method: 'server.echo', params: { message: 'ping' } })
      isAuthenticated.value = true
      sessionValidated = true
      
      await initializeData()
      
      connectWebSocket().catch((err) => {
        console.warn('[Store] WebSocket reconnection failed, will retry:', err)
        isReconnecting.value = true
      })
      
      return true
    } catch (err) {
      console.error('[Store] Session check failed:', err)
      localStorage.removeItem('neode-auth')
      isAuthenticated.value = false
      sessionValidated = false
      isWsSubscribed = false
      isConnected.value = false
      isReconnecting.value = false
      wsClient.disconnect()
      return false
    }
  }

  function needsSessionValidation(): boolean {
    return isAuthenticated.value && !sessionValidated
  }

  // Package actions
  async function installPackage(id: string, marketplaceUrl: string, version: string): Promise<string> {
    return rpcClient.installPackage(id, marketplaceUrl, version)
  }

  async function uninstallPackage(id: string): Promise<void> {
    return rpcClient.uninstallPackage(id)
  }

  async function startPackage(id: string): Promise<void> {
    return rpcClient.startPackage(id)
  }

  async function stopPackage(id: string): Promise<void> {
    return rpcClient.stopPackage(id)
  }

  async function restartPackage(id: string): Promise<void> {
    return rpcClient.restartPackage(id)
  }

  // Server actions
  async function updateServer(marketplaceUrl: string): Promise<'updating' | 'no-updates'> {
    return rpcClient.updateServer(marketplaceUrl)
  }

  async function restartServer(): Promise<void> {
    return rpcClient.restartServer()
  }

  async function shutdownServer(): Promise<void> {
    return rpcClient.shutdownServer()
  }

  async function getMetrics(): Promise<Record<string, unknown>> {
    return rpcClient.getMetrics()
  }

  // Marketplace actions
  async function getMarketplace(url: string): Promise<Record<string, unknown>> {
    return rpcClient.getMarketplace(url)
  }

  return {
    // State
    data,
    isAuthenticated,
    isConnected,
    isReconnecting,
    isLoading,
    error,

    // Computed
    serverInfo,
    packages,
    uiData,
    serverName,
    isRestarting,
    isShuttingDown,
    isOffline,

    // Actions
    login,
    completeLoginAfterTotp,
    logout,
    checkSession,
    needsSessionValidation,
    connectWebSocket,
    installPackage,
    uninstallPackage,
    startPackage,
    stopPackage,
    restartPackage,
    updateServer,
    restartServer,
    shutdownServer,
    getMetrics,
    getMarketplace,
  }
})

