import { Router } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import db from '../db/index.js';
import { requireAdmin } from '../middleware/auth.js';
import sharp from 'sharp';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const iconsDir  = path.resolve(__dirname, '../../../data/icons');
const historyDir = path.resolve(iconsDir, 'history');
fs.mkdirSync(iconsDir,   { recursive: true });
fs.mkdirSync(historyDir, { recursive: true });


// ── Suppression fond blanc SVG ────────────────────────────────────────────────

function isNearWhite(color) {
  if (!color) return false;
  const c = color.trim().toLowerCase();
  if (c === 'white' || c === 'snow') return true;
  const s3 = c.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/);
  if (s3) {
    return parseInt(s3[1] + s3[1], 16) >= 240 &&
           parseInt(s3[2] + s3[2], 16) >= 240 &&
           parseInt(s3[3] + s3[3], 16) >= 240;
  }
  const s6 = c.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/);
  if (s6) {
    return parseInt(s6[1], 16) >= 240 &&
           parseInt(s6[2], 16) >= 240 &&
           parseInt(s6[3], 16) >= 240;
  }
  const rgb = c.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
  if (rgb) {
    return parseInt(rgb[1]) >= 240 && parseInt(rgb[2]) >= 240 && parseInt(rgb[3]) >= 240;
  }
  return false;
}

function getFillFromElement(str) {
  const fa = str.match(/\bfill\s*=\s*["']([^"']*)["']/i);
  if (fa) return fa[1];
  const sa = str.match(/\bstyle\s*=\s*["']([^"']*)["']/i);
  if (sa) {
    const fm = sa[1].match(/(?:^|;)\s*fill\s*:\s*([^;]+)/i);
    if (fm) return fm[1].trim();
  }
  return null;
}

function pathCoversCanvas(d, vbW, vbH) {
  if (!vbW || !vbH) return true;
  const nums = (d.match(/[\d.]+/g) || []).map(parseFloat);
  const hasNearZero = nums.some(v => v <= 5);
  const hasMaxX     = nums.some(v => Math.abs(v - vbW) <= 5);
  const hasMaxY     = nums.some(v => Math.abs(v - vbH) <= 5);
  return hasNearZero && hasMaxX && hasMaxY;
}

function stripSvgBackground(svgContent) {
  let out = svgContent;

  // 1. Remove background-color from style="" on <svg>
  out = out.replace(
    /(<svg\b[^>]*)\sstyle="([^"]*)"/i,
    (_, tag, style) => {
      const cleaned = style.split(';')
        .filter(s => !/^\s*background(-color)?\s*:/i.test(s))
        .join(';').replace(/^;+|;+$/g, '');
      return cleaned ? `${tag} style="${cleaned}"` : tag;
    }
  );

  // 2. Remove enable-background (Adobe Illustrator artifact)
  out = out.replace(/\s+enable-background="[^"]*"/gi, '');

  // 3. Parse viewBox for canvas coverage check
  const vbMatch = out.match(/\bviewBox\s*=\s*["']\s*[\d.]+\s+[\d.]+\s+([\d.]+)\s+([\d.]+)\s*["']/i);
  const vbW = vbMatch ? parseFloat(vbMatch[1]) : 0;
  const vbH = vbMatch ? parseFloat(vbMatch[2]) : 0;

  // 4. Remove near-white <rect> elements at origin covering the canvas
  out = out.replace(/<rect(\s[^>]*)?\/?>/gis, (match) => {
    const fill = getFillFromElement(match);
    if (!fill || !isNearWhite(fill)) return match;
    const x = match.match(/\bx\s*=\s*["']?([^"'\s>]+)/i);
    const y = match.match(/\by\s*=\s*["']?([^"'\s>]+)/i);
    if ((x && parseFloat(x[1]) > 5) || (y && parseFloat(y[1]) > 5)) return match;
    const w = match.match(/\bwidth\s*=\s*["']?([^"'\s>]+)/i);
    const h = match.match(/\bheight\s*=\s*["']?([^"'\s>]+)/i);
    if (!w || !h) return match;
    return '';
  });
  out = out.replace(/<\/rect>/gi, '');

  // 5. Remove near-white <path> elements covering the canvas
  //    (raster-trace backgrounds from design tools like GIMP/Inkscape export)
  out = out.replace(/<path\b[^>]*\/?>/gis, (match) => {
    const fill = getFillFromElement(match);
    if (!fill || !isNearWhite(fill)) return match;
    const dAttr = match.match(/\bd\s*=\s*["']([^"']*?)["']/is);
    if (!dAttr) return match;
    if (pathCoversCanvas(dAttr[1], vbW, vbH)) return '';
    return match;
  });
  out = out.replace(/<\/path>/gi, '');

  out = out.replace(/\n{3,}/g, '\n\n');
  return out;
}

// ── Retraitement post-upload ───────────────────────────────────────────────────
// Retourne le chemin final du fichier (peut changer si JPG/WebP converti en PNG)
async function processUploadedFile(filePath) {
  const ext = path.extname(filePath).toLowerCase();

  // ── SVG : suppression fond blanc en pur texte ──────────────────
  if (ext === '.svg') {
    try {
      const original = fs.readFileSync(filePath, 'utf8');
      const cleaned  = stripSvgBackground(original);
      if (cleaned !== original) fs.writeFileSync(filePath, cleaned, 'utf8');
    } catch (e) {
      console.warn('[icons] stripSvgBackground failed:', e.message);
    }
    return filePath;
  }

  // ── Raster (PNG / JPG / WebP) : fond blanc → transparent via sharp ──
  if (['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
    // La transparence nécessite PNG — on convertit si besoin
    const pngPath = filePath.replace(/\.(jpg|jpeg|webp|png)$/i, '.png');
    try {
      const { data, info } = await sharp(filePath)
        .ensureAlpha()
        .raw()
        .toBuffer({ resolveWithObject: true });

      // Rendre transparents les pixels blanc ou quasi-blanc (seuil > 240/255)
      for (let i = 0; i < data.length; i += 4) {
        if (data[i] > 240 && data[i + 1] > 240 && data[i + 2] > 240) {
          data[i + 3] = 0;
        }
      }

      await sharp(data, {
        raw: { width: info.width, height: info.height, channels: 4 },
      }).png({ compressionLevel: 8 }).toFile(pngPath);

      // Supprimer l'original s'il a changé d'extension
      if (pngPath !== filePath) fs.unlinkSync(filePath);

      return pngPath;
    } catch (e) {
      console.warn('[icons] sharp processing failed:', e.message);
      return filePath; // garder le fichier original en cas d'erreur
    }
  }

  return filePath;
}

const router = Router();

// ── Multer ────────────────────────────────────────────────────────────────────
const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, iconsDir),
  filename: (req, file, cb) => {
    const ext  = path.extname(file.originalname).toLowerCase() || '.svg';
    const name = req.params.name || req.body?.name || 'icon';
    cb(null, `icon_${name}_${Date.now()}${ext}`);
  },
});
const upload = multer({
  storage,
  limits: { fileSize: 2 * 1024 * 1024 }, // 2 Mo max
  fileFilter: (_req, file, cb) => {
    const allowed = ['.svg', '.png', '.jpg', '.jpeg', '.webp'];
    if (allowed.includes(path.extname(file.originalname).toLowerCase())) cb(null, true);
    else cb(new Error('Format non supporté — SVG, PNG, JPG ou WebP uniquement'));
  },
});

// ── Validation slug ───────────────────────────────────────────────────────────
function isValidSlug(s) {
  return /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(s) && s.length <= 64;
}

// ── GET /api/icons — liste toutes les icônes (authentifié) ───────────────────
router.get('/', (req, res) => {
  const rows = db.prepare(`
    SELECT id, name, filename, description, created_at, updated_at
    FROM app_icons
    ORDER BY name
  `).all();
  res.json(rows);
});

// ── GET /api/icons/:name — détail d'une icône ────────────────────────────────
router.get('/:name', (req, res) => {
  const icon = db.prepare('SELECT * FROM app_icons WHERE name = ?').get(req.params.name);
  if (!icon) return res.status(404).json({ error: 'Icône introuvable' });
  res.json(icon);
});

// ── GET /api/icons/:name/history — historique des versions ──────────────────
router.get('/:name/history', requireAdmin, (req, res) => {
  const icon = db.prepare('SELECT id FROM app_icons WHERE name = ?').get(req.params.name);
  if (!icon) return res.status(404).json({ error: 'Icône introuvable' });

  const rows = db.prepare(`
    SELECT id, filename, replaced_at
    FROM app_icons_history
    WHERE icon_id = ?
    ORDER BY replaced_at DESC
  `).all(icon.id);
  res.json(rows);
});

// ── POST /api/icons — créer une nouvelle association nom/image ───────────────
router.post('/', requireAdmin, upload.single('file'), async (req, res, next) => {
  try {
    const name = (req.body.name || '').trim().toLowerCase();
    const description = (req.body.description || '').trim() || null;

    if (!name) return res.status(400).json({ error: 'Le nom est requis' });
    if (!isValidSlug(name)) return res.status(400).json({ error: 'Nom invalide — lettres minuscules, chiffres et tirets uniquement' });
    if (!req.file) return res.status(400).json({ error: 'Fichier requis' });

    // Renommer le fichier avec le bon nom maintenant qu'on a le slug
    const ext = path.extname(req.file.originalname).toLowerCase() || '.svg';
    const newFilename = `icon_${name}_${Date.now()}${ext}`;
    fs.renameSync(req.file.path, path.join(iconsDir, newFilename));
    const finalPath = await processUploadedFile(path.join(iconsDir, newFilename));
    const finalFilename = path.basename(finalPath);

    const row = db.prepare(`
      INSERT INTO app_icons (name, filename, description)
      VALUES (?, ?, ?)
      RETURNING *
    `).get(name, finalFilename, description);

    res.status(201).json(row);
  } catch (err) {
    if (err.code === 'SQLITE_CONSTRAINT_UNIQUE') {
      if (req.file) fs.unlinkSync(req.file.path).catch?.(() => {});
      return res.status(409).json({ error: `Le nom "${req.body?.name}" existe déjà` });
    }
    next(err);
  }
});

// ── PUT /api/icons/:name — remplacer l'image (archive l'ancienne) ────────────
router.put('/:name', requireAdmin, upload.single('file'), async (req, res, next) => {
  try {
    const icon = db.prepare('SELECT * FROM app_icons WHERE name = ?').get(req.params.name);
    if (!icon) return res.status(404).json({ error: 'Icône introuvable' });
    if (!req.file) return res.status(400).json({ error: 'Fichier requis' });

    const ext = path.extname(req.file.originalname).toLowerCase() || '.svg';
    const newFilename = `icon_${icon.name}_${Date.now()}${ext}`;
    fs.renameSync(req.file.path, path.join(iconsDir, newFilename));
    const finalPath = await processUploadedFile(path.join(iconsDir, newFilename));
    const finalFilename = path.basename(finalPath);

    const doReplace = db.transaction(() => {
      // Archiver l'ancienne version
      db.prepare(`
        INSERT INTO app_icons_history (icon_id, filename, replaced_at)
        VALUES (?, ?, datetime('now'))
      `).run(icon.id, icon.filename);

      // Mettre à jour l'entrée principale
      return db.prepare(`
        UPDATE app_icons SET filename = ?, updated_at = datetime('now')
        WHERE id = ?
        RETURNING *
      `).get(finalFilename, icon.id);
    });

    const updated = doReplace();

    // Supprimer les fichiers d'historique au-delà de 10 versions
    const old = db.prepare(`
      SELECT id, filename FROM app_icons_history
      WHERE icon_id = ?
      ORDER BY replaced_at DESC
      LIMIT -1 OFFSET 10
    `).all(icon.id);
    for (const o of old) {
      fs.unlink(path.join(iconsDir, o.filename), () => {});
      db.prepare('DELETE FROM app_icons_history WHERE id = ?').run(o.id);
    }

    res.json(updated);
  } catch (err) {
    next(err);
  }
});

// ── PATCH /api/icons/:name — modifier description uniquement ─────────────────
router.patch('/:name', requireAdmin, (req, res) => {
  const { description } = req.body;
  const updated = db.prepare(`
    UPDATE app_icons SET description = ?, updated_at = datetime('now')
    WHERE name = ?
    RETURNING *
  `).get(description ?? null, req.params.name);
  if (!updated) return res.status(404).json({ error: 'Icône introuvable' });
  res.json(updated);
});

export default router;
