const express = require('express');
const multer  = require('multer');
const path    = require('path');
const fs      = require('fs');
const cors    = require('cors');
const crypto  = require('crypto');
const { v4: uuidv4 } = require('uuid');

const app  = express();
const PORT = process.env.PORT || 3000;
const FIRMWARE_DIR = path.join(__dirname, 'uploads');
const META_FILE    = path.join(FIRMWARE_DIR, '_metadata.json');
const ALLOWED_ORIGIN = process.env.ALLOWED_ORIGIN || '*';

if (!fs.existsSync(FIRMWARE_DIR)) {
  fs.mkdirSync(FIRMWARE_DIR, { recursive: true });
}

// ── Middleware ──────────────────────────────────────────────────────────────
app.use(cors({ origin: ALLOWED_ORIGIN }));
app.use(express.json());

// 펌웨어 바이너리 정적 서빙 (nginx 없이 직접 접근 시 사용)
app.use('/firmware/files', express.static(FIRMWARE_DIR));

// ── Firmware metadata (파일 기반, MVP용) ────────────────────────────────────
function loadMeta() {
  if (!fs.existsSync(META_FILE)) return [];
  try { return JSON.parse(fs.readFileSync(META_FILE, 'utf8')); }
  catch { return []; }
}

function saveMeta(list) {
  fs.writeFileSync(META_FILE, JSON.stringify(list, null, 2));
}

// ── Multer 설정 ─────────────────────────────────────────────────────────────
const storage = multer.diskStorage({
  destination: FIRMWARE_DIR,
  filename: (_req, file, cb) => {
    const safe = Date.now() + '-' + crypto.randomBytes(4).toString('hex');
    cb(null, safe + path.extname(file.originalname));
  },
});

const upload = multer({
  storage,
  fileFilter: (_req, file, cb) => {
    if (path.extname(file.originalname).toLowerCase() === '.bin') {
      cb(null, true);
    } else {
      cb(new Error('.bin 파일만 업로드 가능합니다'));
    }
  },
  limits: { fileSize: 32 * 1024 * 1024 }, // 32 MB
});

// ── 라우트 ──────────────────────────────────────────────────────────────────

// 헬스 체크
app.get('/api/health', (_req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// 펌웨어 목록
app.get('/api/firmware', (_req, res) => {
  res.json(loadMeta());
});

// 펌웨어 업로드
// 지원 필드: firmware(필수), bootloader(선택), partitions(선택), boot_app0(선택)
app.post(
  '/api/firmware/upload',
  upload.fields([
    { name: 'firmware',   maxCount: 1 },
    { name: 'bootloader', maxCount: 1 },
    { name: 'partitions', maxCount: 1 },
    { name: 'boot_app0',  maxCount: 1 },
  ]),
  (req, res) => {
    try {
      const files = req.files || {};
      if (!files.firmware) {
        return res.status(400).json({ error: '펌웨어 파일(firmware)이 필요합니다' });
      }

      const { name, version, description, chipFamily, flashAddress } = req.body;
      const list = loadMeta();
      const id   = uuidv4();

      const parts = [];

      if (files.bootloader) {
        parts.push({
          file: files.bootloader[0].filename,
          offset: '0x0000',
          label: 'Bootloader',
        });
      }

      if (files.partitions) {
        parts.push({
          file: files.partitions[0].filename,
          offset: '0x8000',
          label: 'Partition Table',
        });
      }

      if (files.boot_app0) {
        parts.push({
          file: files.boot_app0[0].filename,
          offset: '0xe000',
          label: 'Boot App0',
        });
      }

      // 부트로더가 따로 없으면(병합 바이너리) 0x0, 있으면 0x10000
      const appOffset = flashAddress
        || (files.bootloader ? '0x10000' : '0x0000');

      parts.push({
        file: files.firmware[0].filename,
        offset: appOffset,
        label: 'Application',
        size: files.firmware[0].size,
      });

      const entry = {
        id,
        name:        name        || '이름 없음',
        version:     version     || '1.0.0',
        description: description || '',
        chipFamily:  chipFamily  || 'ESP32-S3',
        createdAt:   new Date().toISOString(),
        parts,
      };

      list.unshift(entry);
      saveMeta(list);

      res.json({ success: true, id, firmware: entry });
    } catch (err) {
      res.status(500).json({ error: err.message });
    }
  }
);

// esp-web-tools manifest.json 생성
app.get('/api/firmware/:id/manifest', (req, res) => {
  const firmware = loadMeta().find(f => f.id === req.params.id);
  if (!firmware) return res.status(404).json({ error: '펌웨어를 찾을 수 없습니다' });

  // esp-web-tools manifest 스펙
  const manifest = {
    name:    firmware.name,
    version: firmware.version,
    new_install_prompt_erase: true,
    builds: [{
      chipFamily: firmware.chipFamily,
      parts: firmware.parts.map(p => ({
        path:   `/firmware/files/${p.file}`,
        offset: parseInt(p.offset, 16),
      })),
    }],
  };

  // CORS 허용 헤더 (esp-web-tools가 fetch할 때 필요)
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.json(manifest);
});

// 단일 펌웨어 삭제
app.delete('/api/firmware/:id', (req, res) => {
  const list  = loadMeta();
  const index = list.findIndex(f => f.id === req.params.id);
  if (index === -1) return res.status(404).json({ error: '펌웨어를 찾을 수 없습니다' });

  list[index].parts.forEach(p => {
    const fp = path.join(FIRMWARE_DIR, p.file);
    if (fs.existsSync(fp)) fs.unlinkSync(fp);
  });

  list.splice(index, 1);
  saveMeta(list);
  res.json({ success: true });
});

// ── 오류 핸들러 ─────────────────────────────────────────────────────────────
app.use((err, _req, res, _next) => {
  console.error(err.message);
  res.status(400).json({ error: err.message });
});

app.listen(PORT, () => {
  console.log(`ESP32 Flasher Backend  →  http://localhost:${PORT}`);
});
