import express from 'express'
import cors from 'cors'
import multer from 'multer'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
import { existsSync, mkdirSync } from 'fs'
import Database from 'better-sqlite3'
import { v4 as uuidv4 } from 'uuid'
import compression from 'compression'
import helmet from 'helmet'

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

const app = express()
const PORT = process.env.PORT || 3001

// Middleware
app.use(helmet({
  crossOriginResourcePolicy: { policy: "cross-origin" }
}))
app.use(compression())
app.use(cors())
app.use(express.json())
app.use(express.static(join(__dirname, '../dist')))

// Create uploads directory
const uploadsDir = join(__dirname, 'uploads')
const modelsDir = join(uploadsDir, 'models')
if (!existsSync(modelsDir)) {
  mkdirSync(modelsDir, { recursive: true })
}

// Database setup
const db = new Database(join(__dirname, 'database.sqlite'))
db.pragma('journal_mode = WAL')

// Create tables
db.exec(`
  CREATE TABLE IF NOT EXISTS models (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    description TEXT,
    format TEXT NOT NULL,
    filename TEXT NOT NULL,
    file_size INTEGER,
    thumbnail_url TEXT,
    metadata TEXT,
    share_id TEXT UNIQUE,
    user_id TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );

  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    email TEXT UNIQUE,
    name TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );

  CREATE INDEX IF NOT EXISTS idx_models_share_id ON models(share_id);
  CREATE INDEX IF NOT EXISTS idx_models_user_id ON models(user_id);
`)

// File upload configuration
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, modelsDir)
  },
  filename: (req, file, cb) => {
    const uniqueName = `${uuidv4()}-${file.originalname}`
    cb(null, uniqueName)
  }
})

const upload = multer({
  storage,
  limits: { fileSize: 500 * 1024 * 1024 }, // 500MB max
  fileFilter: (req, file, cb) => {
    const allowedExtensions = ['.glb', '.gltf', '.obj', '.fbx', '.ply', '.las', '.laz', '.splat']
    const ext = file.originalname.toLowerCase().substring(file.originalname.lastIndexOf('.'))
    if (allowedExtensions.includes(ext)) {
      cb(null, true)
    } else {
      cb(new Error(`File type ${ext} not allowed. Supported: ${allowedExtensions.join(', ')}`))
    }
  }
})

// Routes

// Health check
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() })
})

// Get all models
app.get('/api/models', (req, res) => {
  try {
    const models = db.prepare(`
      SELECT id, name, description, format, file_size, thumbnail_url, 
             metadata, share_id, created_at, updated_at
      FROM models
      ORDER BY created_at DESC
    `).all()
    
    res.json(models.map(model => ({
      ...model,
      metadata: model.metadata ? JSON.parse(model.metadata) : null,
      url: `/api/models/${model.id}/file`,
      shareUrl: model.share_id ? `/view/${model.share_id}` : null
    })))
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
})

// Get single model
app.get('/api/models/:id', (req, res) => {
  try {
    const model = db.prepare(`
      SELECT * FROM models WHERE id = ? OR share_id = ?
    `).get(req.params.id, req.params.id)
    
    if (!model) {
      return res.status(404).json({ error: 'Model not found' })
    }
    
    res.json({
      ...model,
      metadata: model.metadata ? JSON.parse(model.metadata) : null,
      url: `/api/models/${model.id}/file`,
      shareUrl: model.share_id ? `/view/${model.share_id}` : null
    })
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
})

// Upload model
app.post('/api/models', upload.single('file'), (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded' })
    }

    const modelId = uuidv4()
    const shareId = uuidv4().split('-')[0] // Short share ID
    
    const model = {
      id: modelId,
      name: req.body.name || req.file.originalname.replace(/\.[^/.]+$/, ''),
      description: req.body.description || null,
      format: req.file.originalname.split('.').pop().toLowerCase(),
      filename: req.file.filename,
      file_size: req.file.size,
      thumbnail_url: null,
      metadata: req.body.metadata || null,
      share_id: shareId,
      user_id: req.body.user_id || null
    }

    db.prepare(`
      INSERT INTO models (id, name, description, format, filename, file_size, 
                         thumbnail_url, metadata, share_id, user_id)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      model.id,
      model.name,
      model.description,
      model.format,
      model.filename,
      model.file_size,
      model.thumbnail_url,
      model.metadata,
      model.share_id,
      model.user_id
    )

    res.status(201).json({
      ...model,
      url: `/api/models/${model.id}/file`,
      shareUrl: `/view/${model.share_id}`
    })
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
})

// Serve model file
app.get('/api/models/:id/file', (req, res) => {
  try {
    const model = db.prepare('SELECT filename FROM models WHERE id = ?').get(req.params.id)
    
    if (!model) {
      return res.status(404).json({ error: 'Model not found' })
    }

    const filePath = join(modelsDir, model.filename)
    
    // Set appropriate headers
    const ext = model.filename.split('.').pop().toLowerCase()
    const contentType = {
      'glb': 'model/gltf-binary',
      'gltf': 'model/gltf+json',
      'obj': 'model/obj',
      'fbx': 'application/octet-stream',
      'ply': 'application/octet-stream',
      'las': 'application/octet-stream',
      'laz': 'application/octet-stream',
      'splat': 'application/octet-stream'
    }[ext] || 'application/octet-stream'

    res.setHeader('Content-Type', contentType)
    res.setHeader('Content-Disposition', `inline; filename="${model.filename}"`)
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.sendFile(filePath)
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
})

// Delete model
app.delete('/api/models/:id', async (req, res) => {
  try {
    const model = db.prepare('SELECT filename FROM models WHERE id = ?').get(req.params.id)
    
    if (!model) {
      return res.status(404).json({ error: 'Model not found' })
    }

    // Delete file
    const fs = await import('fs/promises')
    const filePath = join(modelsDir, model.filename)
    try {
      await fs.unlink(filePath)
    } catch (err) {
      console.warn('File not found:', filePath)
    }

    // Delete from database
    db.prepare('DELETE FROM models WHERE id = ?').run(req.params.id)

    res.json({ success: true })
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
})

// Share link route (for public viewing)
app.get('/view/:shareId', (req, res) => {
  res.sendFile(join(__dirname, '../dist/index.html'))
})

// Serve frontend for all other routes
app.get('*', (req, res) => {
  res.sendFile(join(__dirname, '../dist/index.html'))
})

// Start server
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`)
  console.log(`📁 Uploads directory: ${modelsDir}`)
})

