#!/usr/bin/env node

/**
 * Archipelago Mock Backend Server
 * Pure Archipelago implementation - NO StartOS dependencies
 * Supports dev modes: setup, onboarding, existing
 */

import express from 'express'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import { WebSocketServer } from 'ws'
import http from 'http'
import { exec } from 'child_process'
import { promisify } from 'util'
import fs from 'fs/promises'
import path from 'path'
import { fileURLToPath } from 'url'
import Docker from 'dockerode'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const execPromise = promisify(exec)
const docker = new Docker()

const app = express()
const PORT = 5959

// Dev mode from environment (setup, onboarding, existing, or default)
const DEV_MODE = process.env.VITE_DEV_MODE || 'default'

// CORS configuration
const corsOptions = {
  credentials: true,
  origin: (origin, callback) => {
    if (!origin) return callback(null, true)
    callback(null, true)
  }
}

app.use(cors(corsOptions))
app.use(express.json())
app.use(cookieParser())

// Mock session storage
const sessions = new Map()
const MOCK_PASSWORD = 'password123'

// User state (simulated file-based storage)
let userState = {
  setupComplete: false,
  onboardingComplete: false,
  passwordHash: null, // In real app, this would be bcrypt hash
}

// Initialize user state based on dev mode
function initializeUserState() {
  switch (DEV_MODE) {
    case 'setup':
      // Setup mode: Original StartOS node setup - user needs to set password
      // This is the simple password setup, NOT the experimental onboarding
      userState = {
        setupComplete: false, // User hasn't set password yet
        onboardingComplete: false, // Onboarding not relevant for setup mode
        passwordHash: null,
      }
      break
    case 'onboarding':
      // Onboarding mode: Experimental onboarding flow
      // User has set password (via setup) but needs to go through experimental onboarding
      userState = {
        setupComplete: true, // Password already set
        onboardingComplete: false, // Needs experimental onboarding
        passwordHash: MOCK_PASSWORD,
      }
      break
    case 'existing':
      // Existing user: Fully set up, just needs to login
      userState = {
        setupComplete: true,
        onboardingComplete: true,
        passwordHash: MOCK_PASSWORD,
      }
      break
    default:
      // Default: Fully set up (for UI development)
      userState = {
        setupComplete: true,
        onboardingComplete: true,
        passwordHash: MOCK_PASSWORD,
      }
  }
  console.log(`[Auth] Dev mode: ${DEV_MODE}`)
  console.log(`[Auth] Setup: ${userState.setupComplete}, Onboarding: ${userState.onboardingComplete}`)
}

initializeUserState()

// WebSocket clients for broadcasting updates
const wsClients = new Set()

// Helper: Broadcast data update to all WebSocket clients
function broadcastUpdate(patch) {
  const message = JSON.stringify({
    rev: Date.now(),
    patch: patch
  })
  wsClients.forEach(client => {
    if (client.readyState === 1) { // OPEN
      client.send(message)
    }
  })
}

// Track used ports and running containers
const usedPorts = new Set([5959, 8100])
const runningContainers = new Map()

// Predefined port mappings for known apps
const portMappings = {
  'atob': 8102,
  'k484': 8103,
  'amin': 8104
}

// Helper: Query real Docker containers
async function getDockerContainers() {
  try {
    const containers = await docker.listContainers({ all: true })
    
    // Map of container names to app IDs
    const containerMapping = {
      'archy-bitcoin': 'bitcoin',
      'archy-btcpay': 'btcpay-server',
      'archy-homeassistant': 'homeassistant',
      'archy-grafana': 'grafana',
      'archy-endurain': 'endurain',
      'archy-fedimint': 'fedimint',
      'archy-morphos': 'morphos-server',
      'archy-lnd': 'lightning-stack',
      'archy-mempool-web': 'mempool',
      'mempool-electrs': 'mempool-electrs',
      'archy-ollama': 'ollama',
      'archy-searxng': 'searxng',
      'archy-onlyoffice': 'onlyoffice',
      'archy-penpot-frontend': 'penpot'
    }

    const apps = {}
    
    for (const container of containers) {
      const name = container.Names[0].replace(/^\//, '')
      const appId = containerMapping[name]
      
      if (!appId) continue
      
      const isRunning = container.State === 'running'
      const ports = container.Ports || []
      const hostPort = ports.find(p => p.PublicPort)?.PublicPort || null
      
      // Get app metadata
      const appMetadata = {
        'bitcoin': {
          title: 'Bitcoin Core',
          icon: '/assets/img/app-icons/bitcoin.svg',
          description: 'Full Bitcoin node implementation'
        },
        'btcpay-server': {
          title: 'BTCPay Server',
          icon: '/assets/img/app-icons/btcpay-server.png',
          description: 'Self-hosted Bitcoin payment processor'
        },
        'homeassistant': {
          title: 'Home Assistant',
          icon: '/assets/img/app-icons/homeassistant.png',
          description: 'Open source home automation platform'
        },
        'grafana': {
          title: 'Grafana',
          icon: '/assets/img/grafana.png',
          description: 'Analytics and monitoring platform'
        },
        'endurain': {
          title: 'Endurain',
          icon: '/assets/img/endurain.png',
          description: 'Application platform'
        },
        'fedimint': {
          title: 'Fedimint',
          icon: '/assets/img/app-icons/fedimint.png',
          description: 'Federated Bitcoin mint'
        },
        'morphos-server': {
          title: 'MorphOS Server',
          icon: '/assets/img/morphos.png',
          description: 'Server platform'
        },
        'lightning-stack': {
          title: 'Lightning Stack',
          icon: '/assets/img/app-icons/lightning-stack.png',
          description: 'Lightning Network (LND)'
        },
        'mempool': {
          title: 'Mempool',
          icon: '/assets/img/app-icons/mempool.png',
          description: 'Bitcoin blockchain explorer'
        },
        'mempool-electrs': {
          title: 'Electrs',
          icon: '/assets/img/app-icons/electrs.svg',
          description: 'Electrum protocol indexer for Bitcoin'
        },
        'ollama': {
          title: 'Ollama',
          icon: '/assets/img/app-icons/ollama.png',
          description: 'Run large language models locally'
        },
        'searxng': {
          title: 'SearXNG',
          icon: '/assets/img/app-icons/searxng.png',
          description: 'Privacy-respecting metasearch engine'
        },
        'onlyoffice': {
          title: 'OnlyOffice',
          icon: '/assets/img/onlyoffice.webp',
          description: 'Office suite and document collaboration'
        },
        'penpot': {
          title: 'Penpot',
          icon: '/assets/img/penpot.webp',
          description: 'Open-source design and prototyping'
        }
      }

      const metadata = appMetadata[appId] || {
        title: appId,
        icon: '/assets/img/favico.png',
        description: `${appId} application`
      }

      apps[appId] = {
        title: metadata.title,
        version: '1.0.0',
        status: isRunning ? 'running' : 'stopped',
        state: isRunning ? 'running' : 'stopped',
        'static-files': {
          license: 'MIT',
          instructions: metadata.description,
          icon: metadata.icon
        },
        manifest: {
          id: appId,
          title: metadata.title,
          version: '1.0.0',
          description: {
            short: metadata.description,
            long: metadata.description
          },
          'release-notes': 'Initial release',
          license: 'MIT',
          'wrapper-repo': '#',
          'upstream-repo': '#',
          'support-site': '#',
          'marketing-site': '#',
          'donation-url': null,
          interfaces: hostPort ? {
            main: {
              name: 'Web Interface',
              description: `${metadata.title} web interface`,
              ui: true
            }
          } : {}
        },
        installed: {
          'current-dependents': {},
          'current-dependencies': {},
          'last-backup': null,
          'interface-addresses': hostPort ? {
            main: {
              'tor-address': `${appId}.onion`,
              'lan-address': `http://localhost:${hostPort}`
            }
          } : {},
          status: isRunning ? 'running' : 'stopped'
        }
      }
    }
    
    return apps
  } catch (error) {
    console.error('[Docker] Error querying containers:', error.message)
    return {}
  }
}

// Helper: Check if Docker/Podman is available
async function isContainerRuntimeAvailable() {
  try {
    // Try Podman first (Archipelago's choice)
    await execPromise('podman ps')
    return { available: true, runtime: 'podman' }
  } catch {
    try {
      // Fallback to Docker
      await execPromise('docker ps')
      return { available: true, runtime: 'docker' }
    } catch {
      return { available: false, runtime: null }
    }
  }
}

// Helper: Install package with container runtime (if available) or simulate
async function installPackage(id, manifestUrl) {
  console.log(`[Package] 📦 Installing ${id}...`)
  
  try {
    // Check if already installed
    if (mockData['package-data'][id]) {
      throw new Error(`Package ${id} is already installed`)
    }
    
    const version = '0.1.0'
    const runtime = await isContainerRuntimeAvailable()
    
    // Get package metadata
    const packageMetadata = {
      'atob': {
        title: 'A to B Bitcoin',
        shortDesc: 'Bitcoin tools and services for seamless transactions',
        longDesc: 'A to B Bitcoin provides tools and services for Bitcoin transactions.',
        icon: '/assets/img/atob.png'
      },
      'k484': {
        title: 'K484',
        shortDesc: 'Point of Sale and Admin system',
        longDesc: 'K484 provides a complete POS and administration system.',
        icon: '/assets/img/k484.png'
      },
      'amin': {
        title: 'Amin',
        shortDesc: 'Administrative interface for Archipelago',
        longDesc: 'Amin provides administrative tools and monitoring.',
        icon: '/assets/img/favico.png'
      }
    }
    
    const metadata = packageMetadata[id] || {
      title: id.charAt(0).toUpperCase() + id.slice(1),
      shortDesc: `${id} application`,
      longDesc: `${id} application for Archipelago`,
      icon: '/assets/img/favico.png'
    }
    
    // Determine port
    const assignedPort = portMappings[id] || 8105
    usedPorts.add(assignedPort)
    
    let containerMode = false
    let actuallyRunning = false
    
    // Try to run with container runtime if available
    if (runtime.available) {
      try {
        console.log(`[Package] 🐳 ${runtime.runtime} available, attempting to run container...`)
        
        const containerName = `${id}-archipelago`
        const stopCmd = runtime.runtime === 'podman' 
          ? `podman stop ${containerName} 2>/dev/null || true`
          : `docker stop ${containerName} 2>/dev/null || true`
        const rmCmd = runtime.runtime === 'podman'
          ? `podman rm ${containerName} 2>/dev/null || true`
          : `docker rm ${containerName} 2>/dev/null || true`
        
        // Stop and remove existing container if it exists
        await execPromise(stopCmd)
        await execPromise(rmCmd)
        
        // Check if image exists
        const imageCheckCmd = runtime.runtime === 'podman'
          ? `podman images -q ${id}:${version}`
          : `docker images -q ${id}:${version}`
        
        let { stdout } = await execPromise(imageCheckCmd)
        
        if (stdout.trim()) {
          // Image exists, start container
          const runCmd = runtime.runtime === 'podman'
            ? `podman run -d --name ${containerName} -p ${assignedPort}:80 ${id}:${version}`
            : `docker run -d --name ${containerName} -p ${assignedPort}:80 ${id}:${version}`
          
          await execPromise(runCmd)
          
          // Wait for container to be ready
          await new Promise(resolve => setTimeout(resolve, 2000))
          
          // Verify container is running
          const statusCmd = runtime.runtime === 'podman'
            ? `podman ps --filter name=${containerName} --format "{{.Status}}"`
            : `docker ps --filter name=${containerName} --format "{{.Status}}"`
          
          const { stdout: containerStatus } = await execPromise(statusCmd)
          
          if (containerStatus.includes('Up')) {
            containerMode = true
            actuallyRunning = true
            runningContainers.set(id, { 
              port: assignedPort, 
              containerId: containerName, 
              runtime: runtime.runtime 
            })
            console.log(`[Package] 🐳 ${runtime.runtime} container running on port ${assignedPort}`)
          }
        } else {
          console.log(`[Package] ℹ️  Container image ${id}:${version} not found, using simulation mode`)
        }
      } catch (containerError) {
        console.log(`[Package] ⚠️  Container error (${containerError.message}), falling back to simulation`)
      }
    } else {
      console.log(`[Package] ℹ️  Container runtime not available, using simulation mode`)
    }
    
    // If container didn't work, simulate installation
    if (!containerMode) {
      await new Promise(resolve => setTimeout(resolve, 1500))
      runningContainers.set(id, { port: assignedPort, containerId: null, runtime: null })
    }
    
    // Add to mock data
    mockData['package-data'][id] = {
      title: metadata.title,
      version: version,
      status: 'running',
      state: 'running',
      port: assignedPort,
      containerMode: containerMode,
      actuallyRunning: actuallyRunning,
      manifest: {
        id: id,
        title: metadata.title,
        version: version,
        description: {
          short: metadata.shortDesc,
          long: metadata.longDesc
        },
        icon: metadata.icon,
        interfaces: {
          main: {
            name: 'Web Interface',
            description: `${metadata.title} web interface`,
            ui: true,
          },
        },
      },
    }
    
    // Broadcast update
    broadcastUpdate([
      {
        op: 'add',
        path: `/package-data/${id}`,
        value: mockData['package-data'][id]
      }
    ])
    
    if (containerMode) {
      console.log(`[Package] ✅ ${id} installed and RUNNING at http://localhost:${assignedPort}`)
    } else {
      console.log(`[Package] ✅ ${id} installed (simulated)`)
    }
    
    return { success: true, port: assignedPort, containerMode }
    
  } catch (error) {
    console.error(`[Package] ❌ Installation failed:`, error.message)
    throw error
  }
}

// Helper: Uninstall package
async function uninstallPackage(id) {
  console.log(`[Package] 🗑️  Uninstalling ${id}...`)
  
  try {
    if (staticDevApps[id]) {
      throw new Error(`${id} is a demo app and cannot be uninstalled`)
    }
    if (!mockData['package-data'][id]) {
      throw new Error(`Package ${id} is not installed`)
    }
    
    // Stop container if it's running
    const containerInfo = runningContainers.get(id)
    if (containerInfo && containerInfo.containerId) {
      try {
        const runtime = containerInfo.runtime || 'docker'
        const stopCmd = runtime === 'podman'
          ? `podman stop ${containerInfo.containerId} 2>/dev/null || true`
          : `docker stop ${containerInfo.containerId} 2>/dev/null || true`
        const rmCmd = runtime === 'podman'
          ? `podman rm ${containerInfo.containerId} 2>/dev/null || true`
          : `docker rm ${containerInfo.containerId} 2>/dev/null || true`
        
        console.log(`[Package] 🐳 Stopping container ${containerInfo.containerId}...`)
        await execPromise(stopCmd)
        await execPromise(rmCmd)
        console.log(`[Package] 🐳 Container stopped`)
      } catch (error) {
        console.log(`[Package] ⚠️  Error stopping container: ${error.message}`)
      }
    }
    
    await new Promise(resolve => setTimeout(resolve, 1000))
    
    const port = mockData['package-data'][id].port
    if (port) {
      usedPorts.delete(port)
    }
    
    runningContainers.delete(id)
    delete mockData['package-data'][id]
    
    broadcastUpdate([
      {
        op: 'remove',
        path: `/package-data/${id}`
      }
    ])
    
    console.log(`[Package] ✅ ${id} uninstalled successfully`)
    return { success: true }
    
  } catch (error) {
    console.error(`[Package] ❌ Uninstall failed:`, error.message)
    throw error
  }
}

// Mock data
const mockData = {
  'server-info': {
    id: 'archipelago-dev',
    version: '0.1.0',
    name: 'Archipelago Dev Server',
    pubkey: 'mock-pubkey',
    'status-info': {
      restarting: false,
      'shutting-down': false,
      updated: false,
      'backup-progress': null,
      'update-progress': null,
    },
    'lan-address': '192.168.1.100',
    unread: 0,
    'wifi-ssids': [],
    'zram-enabled': false,
  },
  'package-data': {},  // Will be populated from Docker
  ui: {
    name: 'Archipelago',
    'ack-welcome': '0.1.0',
    marketplace: {
      'selected-hosts': [],
      'known-hosts': {},
    },
    theme: 'dark',
  },
}

// Static dev apps (always shown in My Apps when using mock backend)
const staticDevApps = {
  'lorabell': {
    title: 'LoraBell',
    version: '1.0.0',
    status: 'running',
    state: 'running',
    'static-files': {
      license: 'MIT',
      instructions: 'A LoRa based doorbell',
      icon: '/assets/img/app-icons/lorabell.png'
    },
    manifest: {
      id: 'lorabell',
      title: 'LoraBell',
      version: '1.0.0',
      description: {
        short: 'A LoRa based doorbell',
        long: 'A LoRa based doorbell - receive doorbell notifications over LoRa radio.'
      },
      'release-notes': 'Initial release',
      license: 'MIT',
      'wrapper-repo': '#',
      'upstream-repo': '#',
      'support-site': '#',
      'marketing-site': '#',
      'donation-url': null,
      interfaces: {
        main: {
          name: 'Web Interface',
          description: 'LoraBell web interface',
          ui: true
        }
      }
    },
    installed: {
      'current-dependents': {},
      'current-dependencies': {},
      'last-backup': null,
      'interface-addresses': {
        main: {
          'tor-address': 'lorabell.onion',
          'lan-address': 'http://192.168.1.166'
        }
      },
      status: 'running'
    }
  }
}

function mergePackageData(dockerApps) {
  return { ...dockerApps, ...staticDevApps }
}

// Initialize package data from Docker on startup
async function initializePackageData() {
  console.log('[Docker] Querying running containers...')
  const dockerApps = await getDockerContainers()
  mockData['package-data'] = mergePackageData(dockerApps)
  
  const appCount = Object.keys(mockData['package-data']).length
  const runningCount = Object.values(mockData['package-data']).filter(app => app.state === 'running').length
  
  console.log(`[Docker] Found ${appCount} containers (${runningCount} running)`)
  
  if (appCount > 0) {
    console.log('[Docker] Apps detected:')
    Object.entries(mockData['package-data']).forEach(([id, app]) => {
      const port = app.installed?.['interface-addresses']?.main?.['lan-address']
      console.log(`  - ${app.title} (${app.state})${port ? ` → ${port}` : ''}`)
    })
  } else {
    console.log('[Docker] No containers found. Start docker-compose to see apps.')
  }
}

// Handle CORS preflight
app.options('/rpc/v1', (req, res) => {
  res.status(200).end()
})

// RPC endpoint
app.post('/rpc/v1', (req, res) => {
  const { method, params } = req.body
  console.log(`[RPC] ${method}`)

  try {
    switch (method) {
      // Authentication endpoints
      case 'auth.setup': {
        const { password } = params
        
        if (!password || password.length < 8) {
          return res.json({
            error: {
              code: -32602,
              message: 'Password must be at least 8 characters',
            },
          })
        }
        
        // Set up user
        userState.setupComplete = true
        userState.passwordHash = password // In real app, bcrypt hash
        
        console.log(`[Auth] User setup completed`)
        
        return res.json({ result: { success: true } })
      }

      case 'auth.isSetup': {
        return res.json({ result: userState.setupComplete })
      }

      case 'auth.onboardingComplete': {
        userState.onboardingComplete = true
        console.log(`[Auth] Onboarding completed`)
        return res.json({ result: true })
      }

      case 'auth.isOnboardingComplete': {
        return res.json({ result: userState.onboardingComplete })
      }

      case 'auth.resetOnboarding': {
        userState.onboardingComplete = false
        console.log('[Auth] Onboarding reset')
        return res.json({ result: true })
      }

      case 'node.did': {
        const mockDid = 'did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'
        const mockPubkey = 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456'
        return res.json({ result: { did: mockDid, pubkey: mockPubkey } })
      }
      case 'node.nostr-publish': {
        return res.json({ result: { event_id: 'mock-event-id', success: 2, failed: 0 } })
      }
      case 'node.nostr-pubkey': {
        return res.json({ result: { nostr_pubkey: 'mock-nostr-pubkey-hex' } })
      }

      case 'node.signChallenge': {
        const { challenge } = params || {}
        const mockSig = Buffer.from(`mock-sig-${challenge || 'challenge'}`).toString('hex')
        return res.json({ result: { signature: mockSig } })
      }

      case 'node.createBackup': {
        const { passphrase } = params || {}
        if (!passphrase) {
          return res.json({ error: { code: -32602, message: 'Missing passphrase' } })
        }
        const mockDid = 'did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH'
        const mockPubkey = 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456'
        return res.json({
          result: {
            version: 1,
            did: mockDid,
            pubkey: mockPubkey,
            kid: `${mockDid}#key-1`,
            encrypted: true,
            blob: Buffer.from(`mock-encrypted-backup-${passphrase}`).toString('base64'),
            timestamp: new Date().toISOString(),
          },
        })
      }

      case 'auth.login': {
        const { password } = params
        
        if (!userState.setupComplete) {
          return res.json({
            error: {
              code: -32603,
              message: 'User not set up. Please complete setup first.',
            },
          })
        }
        
        // Simple password check (in real app, use bcrypt)
        if (password !== userState.passwordHash && password !== MOCK_PASSWORD) {
          return res.json({
            error: {
              code: -32603,
              message: 'Password Incorrect',
            },
          })
        }

        const sessionId = `session-${Date.now()}`
        sessions.set(sessionId, {
          createdAt: new Date(),
        })

        res.cookie('session', sessionId, {
          httpOnly: true,
          maxAge: 24 * 60 * 60 * 1000,
        })

        return res.json({ result: null })
      }

      case 'auth.logout': {
        const sessionId = req.cookies?.session
        if (sessionId) {
          sessions.delete(sessionId)
        }
        res.clearCookie('session')
        return res.json({ result: null })
      }

      case 'server.echo': {
        return res.json({ result: { message: params?.message || 'Hello from Archipelago!' } })
      }

      case 'server.time': {
        return res.json({
          result: {
            now: new Date().toISOString(),
            uptime: process.uptime(),
          },
        })
      }

      case 'server.metrics': {
        return res.json({
          result: {
            cpu: 45.2,
            memory: 62.8,
            disk: 38.1,
          },
        })
      }

      case 'marketplace.get': {
        const mockApps = [
          {
            id: 'bitcoin',
            title: 'Bitcoin Core',
            description: 'A full Bitcoin node.',
            version: '25.0.0',
            icon: '/assets/img/bitcoin.svg',
            author: 'Bitcoin Core Team',
            license: 'MIT',
          },
          {
            id: 'lightning',
            title: 'Core Lightning',
            description: 'Lightning Network implementation.',
            version: '23.08',
            icon: '/assets/img/c-lightning.png',
            author: 'Blockstream',
            license: 'MIT',
          },
        ]
        
        return res.json({ result: mockApps })
      }

      case 'server.update':
      case 'server.restart':
      case 'server.shutdown': {
        return res.json({ result: 'ok' })
      }

      case 'package.install': {
        const { id, url } = params
        
        installPackage(id, url).catch(err => {
          console.error(`[RPC] Installation failed:`, err.message)
        })
        
        return res.json({ result: `job-${Date.now()}` })
      }

      case 'package.uninstall': {
        const { id } = params
        
        uninstallPackage(id).catch(err => {
          console.error(`[RPC] Uninstall failed:`, err.message)
        })
        
        return res.json({ result: 'ok' })
      }

      case 'package.start':
      case 'package.stop':
      case 'package.restart': {
        return res.json({ result: 'ok' })
      }

      default: {
        return res.json({
          error: {
            code: -32601,
            message: `Method not found: ${method}`,
          },
        })
      }
    }
  } catch (error) {
    console.error('[RPC Error]', error)
    return res.json({
      error: {
        code: -32603,
        message: error.message,
      },
    })
  }
})

// Health check
app.get('/health', (req, res) => {
  res.status(200).send('healthy')
})

// WebSocket endpoint
const server = http.createServer(app)
const wss = new WebSocketServer({ server, path: '/ws/db' })

wss.on('connection', (ws, req) => {
  console.log('[WebSocket] Client connected from', req.socket.remoteAddress)
  wsClients.add(ws)

  // Set up ping/pong to keep connection alive
  const pingInterval = setInterval(() => {
    if (ws.readyState === 1) { // OPEN
      try {
        ws.ping()
      } catch (err) {
        console.error('[WebSocket] Ping error:', err)
        clearInterval(pingInterval)
      }
    } else {
      clearInterval(pingInterval)
    }
  }, 30000) // Ping every 30 seconds

  // Send initial data immediately
  try {
    ws.send(JSON.stringify({
      type: 'initial',
      data: mockData,
    }))
    console.log('[WebSocket] Initial data sent')
  } catch (err) {
    console.error('[WebSocket] Error sending initial data:', err)
  }

  ws.on('pong', () => {
    // Client responded to ping, connection is alive
  })

  ws.on('message', (message) => {
    // Handle incoming messages if needed
    try {
      const data = JSON.parse(message.toString())
      console.log('[WebSocket] Received message:', data)
    } catch (err) {
      console.error('[WebSocket] Error parsing message:', err)
    }
  })

  ws.on('close', (code, reason) => {
    console.log('[WebSocket] Client disconnected', { code, reason: reason.toString() })
    clearInterval(pingInterval)
    wsClients.delete(ws)
  })

  ws.on('error', (error) => {
    console.error('[WebSocket Error]', error)
    clearInterval(pingInterval)
    wsClients.delete(ws)
  })
})

server.listen(PORT, '0.0.0.0', async () => {
  const runtime = await isContainerRuntimeAvailable()
  
  // Initialize package data from Docker
  await initializePackageData()
  
  console.log(`
╔════════════════════════════════════════════════════════════╗
║                                                            ║
║   🚀 Archipelago Mock Backend Server                      ║
║                                                            ║
║   RPC:       http://localhost:${PORT}/rpc/v1               ║
║   WebSocket: ws://localhost:${PORT}/ws/db                  ║
║                                                            ║
║   Dev Mode:  ${DEV_MODE.padEnd(47)}║
║   Setup:     ${userState.setupComplete ? '✅ Complete' : '❌ Not done'.padEnd(47)}║
║   Onboarding: ${userState.onboardingComplete ? '✅ Complete' : '❌ Not done'.padEnd(46)}║
║                                                            ║
║   Mock Password: ${MOCK_PASSWORD.padEnd(40)}║
║                                                            ║
║   Container Runtime: ${runtime.available ? `✅ ${runtime.runtime}`.padEnd(40) : '❌ Not available'.padEnd(40)}║
║   Docker API:        ✅ Connected                         ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝
  `)
  console.log('Mock backend is running. Press Ctrl+C to stop.\n')
  
  // Periodically update package data from Docker (merge with static dev apps)
  setInterval(async () => {
    const dockerApps = await getDockerContainers()
    mockData['package-data'] = mergePackageData(dockerApps)
    
    // Broadcast update to connected clients
    broadcastUpdate([
      {
        op: 'replace',
        path: '/package-data',
        value: mockData['package-data']
      }
    ])
  }, 5000) // Update every 5 seconds
})

process.on('SIGINT', () => {
  console.log('\n\nShutting down mock backend...')
  server.close(() => {
    console.log('Server stopped.')
    process.exit(0)
  })
})
