// Host-side server. After the module extraction this shrunk from
// ~700 lines (the original editor's server.js) to a thin wrapper
// that mounts the module's middleware + provides the host's own
// upload endpoint.
//
// What stayed in the host
// ───────────────────────
//   • The HTTP server itself (express, port binding, listen)
//   • CORS, rate limiting, logger — host-level concerns
//   • The /api/upload endpoint (host owns upload storage)
//   • SPA static serving for the built frontend
//   • Cleanup of old uploaded files (TTL sweep)
//
// What moved to the module (goods-editor/server)
// ──────────────────────────────────────────────
//   • POST /api/export — the canvas+sharp render pipeline
//   • GET  /api/download/:filename — serve exported PNGs
//   • GET  /api/health — health probe
//   • Font registration

import express from 'express';
import cors from 'cors';
import multer from 'multer';
import { v4 as uuidv4 } from 'uuid';
import sharp from 'sharp';
import canvas from 'canvas';
import rateLimit from 'express-rate-limit';
import pino from 'pino';
import pinoHttp from 'pino-http';
import { fileURLToPath } from 'url';
import { dirname, join, resolve, sep } from 'path';
import { mkdirSync, existsSync, readdirSync, statSync, rmSync } from 'fs';

import { mountEditorApi } from 'goods-editor/server';

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

const app = express();
const PORT = process.env.PORT || 3001;
const IS_PRODUCTION = process.env.NODE_ENV === 'production';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  base: { service: 'apparel-designer' },
});

app.use(pinoHttp({
  logger,
  autoLogging: { ignore: (req) => req.url === '/api/health' },
}));

// ── Filesystem layout ──────────────────────────────────────────────
const uploadsDir = join(__dirname, 'uploads');
const exportsDir = join(__dirname, 'exports');
const fontsDir = join(__dirname, 'node_modules', 'goods-editor', 'fonts');
const stickersDir = IS_PRODUCTION
  ? join(__dirname, 'dist', 'stickers')
  : join(__dirname, 'public', 'stickers');
const uploadsRoot = resolve(uploadsDir);

[uploadsDir, exportsDir].forEach((dir) => {
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
});

// ── TTL cleanup ─────────────────────────────────────────────────────
const FILE_TTL_MS = parseInt(process.env.FILE_TTL_MS || '', 10) || 24 * 60 * 60 * 1000;
const CLEANUP_INTERVAL_MS = parseInt(process.env.CLEANUP_INTERVAL_MS || '', 10) || 60 * 60 * 1000;

function cleanupOldFiles(dir, ttlMs) {
  if (!existsSync(dir)) return 0;
  const now = Date.now();
  let removed = 0;
  let entries = [];
  try { entries = readdirSync(dir); } catch { return 0; }
  for (const entry of entries) {
    const full = join(dir, entry);
    try {
      const stat = statSync(full);
      if (stat.isFile() && now - stat.mtimeMs > ttlMs) {
        rmSync(full);
        removed++;
      } else if (stat.isDirectory()) {
        removed += cleanupOldFiles(full, ttlMs);
      }
    } catch { /* skip */ }
  }
  return removed;
}

function runCleanup() {
  const u = cleanupOldFiles(uploadsDir, FILE_TTL_MS);
  const e = cleanupOldFiles(exportsDir, FILE_TTL_MS);
  if (u > 0 || e > 0) {
    logger.info({ removedUploads: u, removedExports: e, ttlMs: FILE_TTL_MS }, 'Cleanup pass complete');
  }
}
runCleanup();
setInterval(runCleanup, CLEANUP_INTERVAL_MS).unref();

// ── App-level middleware (CORS, JSON, rate limits) ─────────────────
const corsOptions = IS_PRODUCTION
  ? { origin: process.env.CORS_ORIGIN || false }
  : { origin: true };
app.use(cors(corsOptions));
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));

const uploadLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 30,
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: 'Too many upload requests. Please try again later.' },
});

// ── Static serving ─────────────────────────────────────────────────
app.use('/uploads', express.static(uploadsDir));
app.use('/exports', express.static(exportsDir));
if (IS_PRODUCTION) {
  app.use(express.static(join(__dirname, 'dist')));
}

// ── Host-owned: upload endpoint ────────────────────────────────────
const MIME_TO_EXT = {
  'image/jpeg': 'jpg',
  'image/png': 'png',
  'image/webp': 'webp',
};

const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, uploadsDir),
  filename: (_req, file, cb) => {
    const ext = MIME_TO_EXT[file.mimetype] || 'bin';
    cb(null, `${uuidv4()}.${ext}`);
  },
});

const fileFilter = (_req, file, cb) => {
  if (MIME_TO_EXT[file.mimetype]) {
    cb(null, true);
  } else {
    cb(new Error('Invalid file type. Only JPEG, PNG, and WebP are allowed.'), false);
  }
};

const upload = multer({
  storage, fileFilter,
  limits: { fileSize: 20 * 1024 * 1024 },
});

app.post('/api/upload', uploadLimiter, upload.single('image'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded' });
    }
    const originalUrl = `/uploads/${req.file.filename}`;
    const previewFilename = `${uuidv4()}.png`;
    const previewDir = join(uploadsDir, 'preview');
    if (!existsSync(previewDir)) mkdirSync(previewDir, { recursive: true });

    await sharp(req.file.path)
      .resize({ width: 1000, height: 1000, fit: 'inside' })
      .png()
      .toFile(join(previewDir, previewFilename));

    res.json({
      success: true,
      original: {
        url: originalUrl,
        filename: req.file.filename,
        size: req.file.size,
        mimetype: req.file.mimetype,
      },
      preview: {
        url: `/uploads/preview/${previewFilename}`,
        filename: previewFilename,
      },
    });
  } catch (err) {
    req.log?.error({ err: err.message }, 'Upload pipeline failure');
    res.status(500).json({ error: 'Failed to process upload', details: err.message });
  }
});

// ── Module-owned: editor API (export, download, health) ────────────
mountEditorApi(app, {
  canvas,
  sharp,
  exportsDir,
  fontsDir,
  stickersDir,
  uploadsDir,
  basePath: '/api',
  logger,
});

// ── 404 + SPA catch-alls ───────────────────────────────────────────
app.all('/api/*', (_req, res) => {
  res.status(404).json({ error: 'API route not found' });
});

if (IS_PRODUCTION) {
  app.get('*', (_req, res) => {
    res.sendFile(join(__dirname, 'dist', 'index.html'));
  });
}

// ── Error handler ──────────────────────────────────────────────────
app.use((err, req, res, _next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      return res.status(400).json({ error: 'File too large. Maximum size is 20MB.' });
    }
    return res.status(400).json({ error: err.message });
  }
  req.log?.error({ err: err.message, stack: err.stack }, 'Unhandled error');
  res.status(500).json({ error: 'Internal server error', details: err.message });
});

app.listen(PORT, () => {
  logger.info({ port: PORT, mode: IS_PRODUCTION ? 'production' : 'development' }, 'Server listening');
});
