#!/usr/bin/env node

/**
 * HCLI Web - Browser-based IDE
 *
 * Launches the HCLI web server and opens browser.
 */

import path from 'path'
import fs from 'fs'
import { spawn, exec } from 'child_process'
import { fileURLToPath } from 'url'
import { createServer } from 'net'
import { networkInterfaces } from 'os'

// Get __dirname equivalent in ES modules
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// Get local IP address for network access
function getLocalIP() {
  const platform = process.platform
  const nets = networkInterfaces()

  // Collect all valid LAN IPs
  const lanIPs = []

  // Mac/Linux priority order
  const priorityOrderMac = ['en0', 'en1', 'en2', 'eth0', 'eth1', 'Wi-Fi', 'Ethernet']
  // Windows priority order (Chinese and English names)
  const priorityOrderWin = ['Wi-Fi', 'Ethernet', '以太网', '无线网络连接', 'WLAN']

  const priorityOrder = platform === 'darwin' ? priorityOrderMac : priorityOrderWin

  for (const name of Object.keys(nets)) {
    for (const net of nets[name] || []) {
      // Skip internal and non-IPv4 addresses
      if (net.family === 'IPv4' && !net.internal) {
        lanIPs.push({ name, ip: net.address })
      }
    }
  }

  if (lanIPs.length === 0) {
    return 'localhost' // fallback
  }

  // Select by priority order
  for (const priorityName of priorityOrder) {
    const found = lanIPs.find(item => item.name === priorityName || item.name.includes(priorityName))
    if (found) {
      return found.ip
    }
  }

  // Return first valid IP
  return lanIPs[0].ip
}

// Get package version
const packageDir = path.resolve(__dirname, '..')
const pkg = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'))
const version = pkg.version

// Check if port is in use
function isPortInUse(port) {
  return new Promise((resolve) => {
    const server = createServer()
      .once('error', () => resolve(true))
      .once('listening', () => {
        server.close()
        resolve(false)
      })
      .listen(port)
  })
}

// Kill process using the port
async function killProcessOnPort(port) {
  return new Promise((resolve, reject) => {
    const platform = process.platform

    if (platform === 'win32') {
      // Windows: use netstat and taskkill
      const command = `netstat -ano | findstr :${port}`
      exec(command, { shell: 'cmd.exe' }, (error, stdout) => {
        if (error || !stdout.trim()) {
          resolve(false)
          return
        }

        // Parse Windows netstat output
        const lines = stdout.trim().split('\n')
        const pids = []
        lines.forEach(line => {
          const parts = line.trim().split(/\s+/)
          // Format: PROTO  LOCAL ADDRESS  FOREIGN ADDRESS  STATE  PID
          if (parts.length >= 5) {
            const pid = parts[parts.length - 1]
            if (pid && !pids.includes(pid)) {
              pids.push(pid)
            }
          }
        })

        if (pids.length === 0) {
          resolve(false)
          return
        }

        console.log(`[Port] Found process(es) using port ${port}: PID ${pids.join(', ')}`)

        // Kill using taskkill
        let killed = 0
        pids.forEach(pid => {
          try {
            spawn('taskkill', ['/F', '/PID', pid], { stdio: 'ignore' })
            killed++
          } catch (e) {
            // Ignore error
          }
        })

        if (killed > 0) {
          console.log(`[Port] Killed ${killed} process(es) on port ${port}`)
        }

        setTimeout(() => resolve(true), 500)
      })
      return
    }

    // macOS/Linux: lsof to find process
    const command = `lsof -ti:${port}`
    exec(command, (error, stdout) => {
      if (error || !stdout.trim()) {
        resolve(false)
        return
      }

      const pids = stdout.trim().split('\n').filter(Boolean)

      if (pids.length === 0) {
        resolve(false)
        return
      }

      console.log(`[Port] Found process(es) using port ${port}: PID ${pids.join(', ')}`)

      // Kill processes
      let killed = 0
      for (const pid of pids) {
        try {
          process.kill(parseInt(pid, 10), 'SIGTERM')
          killed++
        } catch (e) {
          // Process may already be gone
        }
      }

      if (killed > 0) {
        console.log(`[Port] Killed ${killed} process(es) on port ${port}`)
      }

      setTimeout(() => resolve(true), 500)
    })
  })
}

// Parse arguments
const args = process.argv.slice(2)
let port = 8181
let cwd = process.cwd()
let noOpen = false

for (let i = 0; i < args.length; i++) {
  if (args[i] === '--port' && args[i + 1]) {
    port = parseInt(args[i + 1], 10)
    i++
  } else if (args[i] === '--cwd' && args[i + 1]) {
    cwd = args[i + 1]
    i++
  } else if (args[i] === '--no-open') {
    noOpen = true
  } else if (args[i] === '--version' || args[i] === '-v') {
    console.log(version)
    process.exit(0)
  } else if (args[i] === '--help' || args[i] === '-h') {
    console.log(`
HCLI Web - Browser-based IDE

Usage:
  hstudio [options]

Options:
  --port <number>  Server port (default: 8181)
  --cwd <path>     Working directory (default: current directory)
  --no-open        Don't auto-open browser
  -v, --version    Show version number
  -h, --help       Show this help message

Examples:
  hstudio                      # Start on port 8181
  hstudio --port 3000          # Start on port 3000
  hstudio --cwd /path/to/project  # Set working directory
`)
    process.exit(0)
  }
}

// Server paths
const serverPath = path.join(packageDir, 'server', 'index.js')
const webPath = path.join(packageDir, 'web')

// Check if files exist
if (!fs.existsSync(serverPath)) {
  console.error('Error: Server files not found. Please reinstall the package.')
  console.error('Expected path:', serverPath)
  process.exit(1)
}

if (!fs.existsSync(webPath)) {
  console.error('Error: Web files not found. Please reinstall the package.')
  console.error('Expected path:', webPath)
  process.exit(1)
}

// Main function
async function main() {
  // Get local IP
  const localIP = getLocalIP()
  const host = localIP || 'localhost'

  // Check if port is in use
  if (await isPortInUse(port)) {
    console.log(`[Port] Port ${port} is in use, attempting to kill existing process...`)
    await killProcessOnPort(port)

    // Double-check port is now free
    if (await isPortInUse(port)) {
      console.error(`[Port] Failed to free port ${port}. Please manually kill the process or use a different port.`)
      process.exit(1)
    }
  }

  // Print banner
  console.log('='.repeat(50))
  console.log(`  HStudio Web v${version}`)
  console.log('='.repeat(50))
  console.log('')
  console.log(`  Port: ${port}`)
  console.log(`  CWD:  ${cwd}`)
  console.log(`  URL:  http://${host}:${port}`)
  console.log('')
  console.log('='.repeat(50))

  // Start server
  const server = spawn('node', [serverPath, '--port', port.toString(), '--cwd', cwd], {
    cwd: packageDir,
    stdio: 'inherit',
    env: {
      ...process.env,
      HSTUDIO_PORT: port.toString(),
      HSTUDIO_CWD: cwd,
    },
  })

  // Handle server exit
  server.on('error', (error) => {
    console.error('Error starting server:', error)
    process.exit(1)
  })

  server.on('exit', (code) => {
    if (code !== 0) {
      console.error('Server exited with code:', code)
      process.exit(code || 1)
    }
  })

  // Open browser after a short delay (unless --no-open)
  if (!noOpen) {
    setTimeout(async () => {
      const url = `http://${host}:${port}`

      // Try to open browser using the 'open' package or system commands
      try {
        // Use dynamic import for open package (ES module)
        const { default: open } = await import('open')
        await open(url).catch(() => {
          // Fallback to system commands
          const platform = process.platform
          if (platform === 'darwin') {
            spawn('open', [url])
          } else if (platform === 'win32') {
            spawn('cmd', ['/c', 'start', url])
          } else {
            spawn('xdg-open', [url])
          }
        })
      } catch (e) {
        // open package not available, use system commands
        const platform = process.platform
        if (platform === 'darwin') {
          spawn('open', [url])
        } else if (platform === 'win32') {
          spawn('cmd', ['/c', 'start', url])
        } else if (platform === 'linux') {
          spawn('xdg-open', [url])
        } else {
          console.log(`Please open your browser and navigate to: ${url}`)
        }
      }

      console.log(`Browser opened: ${url}`)
    }, 1000)
  }

  // Handle Ctrl+C
  process.on('SIGINT', () => {
    console.log('\nShutting down...')
    server.kill('SIGTERM')
    process.exit(0)
  })

  process.on('SIGTERM', () => {
    server.kill('SIGTERM')
    process.exit(0)
  })
}

main()
