import express from 'express'
import session from 'express-session'
import { WebSocketServer, WebSocket } from 'ws'
import { createServer } from 'http'
import { v4 as uuidv4 } from 'uuid'
import fs from 'fs'
import path from 'path'

const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// Session kurulumu
app.use(session({
  secret: 'aydin-crm-secret-key-2025',
  resave: false,
  saveUninitialized: false
}))

const publicDir = path.join(process.cwd(), 'public')
const privateDir = path.join(process.cwd(), 'private')

app.use(express.static(publicDir))

// Login Route
app.post('/login', (req, res) => {
  const { username, password } = req.body
  // Sabit kullanıcı bilgileri
  if (username === 'admin' && password === 'admin123') {
    req.session.isLoggedIn = true
    req.session.user = username
    return res.redirect('/')
  }
  res.redirect('/login.html?error=1')
})

app.get('/logout', (req, res) => {
  req.session.destroy()
  res.redirect('/login.html')
})

// Auth Middleware
const requireAuth = (req, res, next) => {
  if (req.session && req.session.isLoggedIn) {
    return next()
  }
  res.redirect('/login.html')
}

// Korumalı route
app.get('/', requireAuth, (req, res) => res.sendFile(path.join(privateDir, 'index.html')))

const server = createServer(app)
const wss = new WebSocketServer({ server })

const devices = new Map() // deviceId -> { ws, deviceName, drives, lastSeen }
const admins = new Set() // admin ws
const downloadsDir = path.join(process.cwd(), 'node-socket', 'downloads')
fs.mkdirSync(downloadsDir, { recursive: true })

function broadcastDevices() {
  const list = [...devices.entries()].map(([deviceId, d]) => ({ deviceId, deviceName: d.deviceName, drives: d.drives, lastSeen: d.lastSeen }))
  const msg = JSON.stringify({ type: 'devices', devices: list })
  admins.forEach(ws => { try { ws.send(msg) } catch { } })
}

wss.on('connection', ws => {
  ws.isAlive = true
  ws.on('pong', () => ws.isAlive = true)

  ws.on('message', data => {
    let msg
    try { msg = JSON.parse(data.toString()) } catch { return }
    if (msg.type === 'agent_hello') {
      const { deviceId, deviceName, drives } = msg
      devices.set(deviceId, { ws, deviceName, drives, lastSeen: Date.now() })
      ws.deviceId = deviceId
      broadcastDevices()
    } else if (msg.type === 'admin_hello') {
      admins.add(ws)
      broadcastDevices()
    } else if (msg.type === 'list_dir') {
      const { deviceId, path: target } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'list_dir', path: target })) } catch {}
      }
    } else if (msg.type === 'get_file') {
      const { deviceId, path: target } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'get_file', path: target })) } catch {}
      }
    } else if (msg.type === 'run_cmd') {
      const { deviceId, command } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'run_cmd', command })) } catch {}
      }
    } else if (msg.type === 'update_agent') {
      const { deviceId, url } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'update_agent', url })) } catch {}
      }
    } else if (msg.type === 'net_scan') {
      const { deviceId } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'net_scan' })) } catch {}
      }
    } else if (msg.type === 'net_shares') {
      const { deviceId, ip } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'net_shares', ip })) } catch {}
      }
    } else if (msg.type === 'put_file_begin') {
      const { deviceId, path } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'put_file_begin', path })) } catch {}
      }
    } else if (msg.type === 'put_file_chunk') {
      const { deviceId, chunk } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'put_file_chunk', chunk })) } catch {}
      }
    } else if (msg.type === 'put_file_end') {
      const { deviceId } = msg
      const d = devices.get(deviceId)
      if (d && d.ws && d.ws.readyState === WebSocket.OPEN) {
        try { d.ws.send(JSON.stringify({ type: 'put_file_end' })) } catch {}
      }
    } else if (msg.type === 'listing') {
      const { deviceId } = msg
      const payload = JSON.stringify({ type: 'listing', ...msg })
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'file_chunk') {
      const { deviceId, path: filePath, chunk } = msg
      const base = path.basename(filePath)
      const dir = path.join(downloadsDir, deviceId)
      fs.mkdirSync(dir, { recursive: true })
      const tmp = path.join(dir, base + '.part')
      fs.appendFileSync(tmp, Buffer.from(chunk, 'base64'))
    } else if (msg.type === 'file_end') {
      const { deviceId, path: filePath } = msg
      const base = path.basename(filePath)
      const dir = path.join(downloadsDir, deviceId)
      const tmp = path.join(dir, base + '.part')
      const final = path.join(dir, base)
      try { fs.renameSync(tmp, final) } catch {}
      const url = `/downloads/${encodeURIComponent(deviceId)}/${encodeURIComponent(base)}`
      const payload = JSON.stringify({ type: 'file_ready', deviceId, path: filePath, url })
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'cmd_output' || msg.type === 'cmd_end') {
      const payload = JSON.stringify(msg)
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'update_status') {
      const payload = JSON.stringify(msg)
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'net_devices') {
      const payload = JSON.stringify(msg)
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'net_shares') {
      const payload = JSON.stringify(msg)
      admins.forEach(a => { try { a.send(payload) } catch {} })
    } else if (msg.type === 'put_file_status' || msg.type === 'put_file_ok' || msg.type === 'put_file_error') {
      const payload = JSON.stringify(msg)
      admins.forEach(a => { try { a.send(payload) } catch {} })
    }
  })

  ws.on('close', () => {
    if (ws.deviceId && devices.has(ws.deviceId)) {
      devices.delete(ws.deviceId)
      broadcastDevices()
    }
    admins.delete(ws)
  })
})

// Admin -> commands
app.post('/api/command/list', (req, res) => {
  const { deviceId, path: target } = req.body
  const d = devices.get(deviceId)
  if (!d) return res.status(404).json({ error: 'device_offline' })
  d.ws.send(JSON.stringify({ type: 'list_dir', path: target }))
  res.json({ status: 'sent' })
})

app.post('/api/command/get-file', (req, res) => {
  const { deviceId, path: target } = req.body
  const d = devices.get(deviceId)
  if (!d) return res.status(404).json({ error: 'device_offline' })
  d.ws.send(JSON.stringify({ type: 'get_file', path: target }))
  res.json({ status: 'sent' })
})

// serve downloads
app.get('/downloads/:deviceId/:name', (req, res) => {
  const file = path.join(downloadsDir, req.params.deviceId, req.params.name)
  if (fs.existsSync(file)) return res.download(file)
  res.status(404).send('Not found')
})

// WS health
setInterval(() => {
  wss.clients.forEach(ws => {
    if (!ws.isAlive) return ws.terminate()
    ws.isAlive = false
    try { ws.ping() } catch {}
  })
}, 30000)

const PORT = 3000
server.listen(PORT, 'sv.ticaretplus.com', () => {
  console.log(`Socket server listening on http://sv.ticaretplus.com:${PORT}/`)
})
