const router = require('express').Router();
const fetch  = require('node-fetch');
const path   = require('path');
const fs     = require('fs');
const sharp  = require('sharp');
const db     = require('../lib/db');

function requireUser(req, res, next) {
  if (!req.session.userId) return res.status(401).json({ error: 'Unauthorized' });
  const user = db.prepare('SELECT * FROM users WHERE id=?').get(req.session.userId);
  if (!user) return res.status(401).json({ error: 'Unauthorized' });
  req.user = user; next();
}

// ── Upload image ──────────────────────────────────────────────────────────────
// Normalises to JPEG 1080×1080 so Instagram always accepts the file.
router.post('/ig/upload', requireUser, async (req, res) => {
  try {
    const { base64, filename, tmpId } = req.body;
    if (!base64 || !tmpId) return res.status(400).json({ error: 'Missing fields' });

    const safeId   = tmpId.replace(/[^a-zA-Z0-9]/g, '');
    const safeName = (filename || 'slide.jpg').replace(/[^a-zA-Z0-9._-]/g, '').replace(/\.\w+$/, '') + '.jpg';
    const dir      = path.join(__dirname, '../public/uploads/tmp', safeId);

    fs.mkdirSync(dir, { recursive: true });
    const raw = Buffer.from(base64.replace(/^data:image\/\w+;base64,/, ''), 'base64');
    if (!raw.length) return res.status(400).json({ error: 'Empty image data' });

    // Convert to JPEG 1080×1350 (4:5 portrait — IG carousel standard)
    const buf = await sharp(raw)
      .resize(1080, 1350, { fit: 'cover', position: 'centre' })
      .jpeg({ quality: 92 })
      .toBuffer();

    fs.writeFileSync(path.join(dir, safeName), buf);

    const proto = req.headers['x-forwarded-proto']?.split(',')[0].trim() || 'https';
    const host  = req.get('host');
    res.json({ url: `${proto}://${host}/api/ig/media/${safeId}/${safeName}` });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// ── Serve temp media through Node.js (bypasses LiteSpeed static-file handling) ─
router.get('/ig/media/:tmpId/:filename', (req, res) => {
  const safeId   = req.params.tmpId.replace(/[^a-zA-Z0-9]/g, '');
  const safeName = req.params.filename.replace(/[^a-zA-Z0-9._-]/g, '');
  if (!safeId || !safeName) return res.status(400).end();

  const filePath = path.join(__dirname, '../public/uploads/tmp', safeId, safeName);
  if (!fs.existsSync(filePath)) return res.status(404).end();

  const stat = fs.statSync(filePath);
  res.setHeader('Content-Type', 'image/jpeg');
  res.setHeader('Content-Length', stat.size);
  res.setHeader('Cache-Control', 'public, max-age=300');
  res.setHeader('Access-Control-Allow-Origin', '*');
  fs.createReadStream(filePath).pipe(res);
});

// ── Re-host image on an external CDN so Meta's servers can download it ────────
// Our server's IP (Namecheap shared) is not reachable by Instagram's API.
// Tries services in order, returns the first working URL.

function buildMultipart(boundary, fieldName, filename, content) {
  const pre = Buffer.from(
    `--${boundary}\r\n` +
    `Content-Disposition: form-data; name="${fieldName}"; filename="${filename}"\r\n` +
    `Content-Type: image/jpeg\r\n\r\n`
  );
  return Buffer.concat([pre, content, Buffer.from(`\r\n--${boundary}--\r\n`)]);
}

// 0x0.st — anonymous file host, field name: "file"
async function uploadTo0x0(content, filename) {
  const boundary = 'b' + Date.now().toString(36);
  const body     = buildMultipart(boundary, 'file', filename, content);
  const r = await fetch('https://0x0.st', {
    method:  'POST',
    headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}`, 'Content-Length': String(body.length) },
    body
  });
  const url = (await r.text()).trim();
  if (!url.startsWith('https://')) throw new Error('0x0: ' + url);
  return url;
}

// transfer.sh — simple PUT upload
async function uploadToTransferSh(content, filename) {
  const r = await fetch(`https://transfer.sh/${encodeURIComponent(filename)}`, {
    method:  'PUT',
    headers: { 'Content-Type': 'application/octet-stream', 'Max-Downloads': '50', 'Max-Days': '1' },
    body:    content
  });
  if (!r.ok) throw new Error(`transfer.sh HTTP ${r.status}`);
  const url = (await r.text()).trim();
  if (!url.startsWith('https://')) throw new Error('transfer.sh: ' + url);
  return url;
}

// litterbox.catbox.moe — temporary file host (24h), field name: "fileToUpload"
async function uploadToLitterbox(content, filename) {
  const boundary = 'b' + Date.now().toString(36);
  const pre = Buffer.from(
    `--${boundary}\r\n` +
    `Content-Disposition: form-data; name="reqtype"\r\n\r\n` +
    `fileupload\r\n` +
    `--${boundary}\r\n` +
    `Content-Disposition: form-data; name="time"\r\n\r\n` +
    `24h\r\n` +
    `--${boundary}\r\n` +
    `Content-Disposition: form-data; name="fileToUpload"; filename="${filename}"\r\n` +
    `Content-Type: image/jpeg\r\n\r\n`
  );
  const body = Buffer.concat([pre, content, Buffer.from(`\r\n--${boundary}--\r\n`)]);
  const r = await fetch('https://litterbox.catbox.moe/resources/internals/api.php', {
    method:  'POST',
    headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}`, 'Content-Length': String(body.length) },
    body
  });
  const url = (await r.text()).trim();
  if (!url.startsWith('https://')) throw new Error('litterbox: ' + url);
  return url;
}

async function uploadToExternalCDN(filePath) {
  const content  = fs.readFileSync(filePath);
  const filename = path.basename(filePath);

  const services = [
    () => uploadToLitterbox(content, filename),  // works — tested
    () => uploadTo0x0(content, filename),
    () => uploadToTransferSh(content, filename),
  ];

  for (const fn of services) {
    try {
      const url = await fn();
      console.log('[uploadToExternalCDN] uploaded to', url);
      return url;
    } catch (e) {
      console.warn('[uploadToExternalCDN] service failed:', e.message);
    }
  }
  console.error('[uploadToExternalCDN] all services failed');
  return null;
}

// Derive local file path from either URL format (/uploads/tmp/... or /api/ig/media/...)
function urlToLocalPath(url) {
  const m = url.match(/\/(?:uploads\/tmp|api\/ig\/media)\/([a-zA-Z0-9]+)\/([a-zA-Z0-9._-]+)$/);
  if (!m) return null;
  return path.join(__dirname, '../public/uploads/tmp', m[1], m[2]);
}

// ── Post to Instagram ─────────────────────────────────────────────────────────
router.post('/ig/post', requireUser, async (req, res) => {
  const { imageUrls, imageUrl, caption, tmpId } = req.body;
  const urls  = imageUrls || (imageUrl ? [imageUrl] : []);
  const token = req.user.ig_token;

  if (!token)        return res.status(400).json({ error: 'Instagram not connected' });
  if (!urls.length)  return res.status(400).json({ error: 'No images provided' });

  // Always fetch user_id as string from API — avoids float precision issues with stored values
  const meRes = await fetch(`https://graph.instagram.com/v21.0/me?fields=id&access_token=${token}`);
  const me    = await meRes.json();
  const uid   = me.id;
  if (!uid) return res.status(400).json({ error: 'Could not resolve Instagram user ID: ' + JSON.stringify(me) });

  // Also fix the stored ig_user_id if it's wrong
  if (req.user.ig_user_id !== uid) {
    db.prepare('UPDATE users SET ig_user_id=? WHERE id=?').run(uid, req.user.id);
  }

  const ig = async (igPath, body) => {
    const r = await fetch(`https://graph.instagram.com/v21.0/${igPath}`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body:    new URLSearchParams({ ...body, access_token: token })
    });
    return r.json();
  };

  const sleep = ms => new Promise(r => setTimeout(r, ms));

  // Re-host on external CDN so Meta's servers can download regardless of our IP
  const resolveImageUrl = async (url) => {
    const filePath = urlToLocalPath(url);
    if (filePath && fs.existsSync(filePath)) {
      const cdnUrl = await uploadToExternalCDN(filePath);
      if (cdnUrl) return cdnUrl;
    }
    console.warn('[resolveImageUrl] all CDNs failed, falling back to direct URL:', url);
    return url;
  };

  try {
    let postId;
    if (urls.length === 1) {
      const imageUrl = await resolveImageUrl(urls[0]);
      const r = await ig(`${uid}/media`, { image_url: imageUrl, caption });
      if (!r.id) throw new Error(JSON.stringify(r));
      await sleep(5000);
      const r2 = await ig(`${uid}/media_publish`, { creation_id: r.id });
      if (!r2.id) throw new Error(JSON.stringify(r2));
      postId = r2.id;
    } else {
      // Carousel — upload each item to Meta CDN, no media_type for photo items
      const children = [];
      for (const url of urls) {
        const imageUrl = await resolveImageUrl(url);
        const r = await ig(`${uid}/media`, { image_url: imageUrl, is_carousel_item: 'true' });
        if (!r.id) throw new Error('Item failed: ' + JSON.stringify(r));
        children.push(r.id);
      }
      await sleep(5000);
      const c = await ig(`${uid}/media`, { media_type: 'CAROUSEL', children: children.join(','), caption });
      if (!c.id) throw new Error(JSON.stringify(c));
      await sleep(5000);
      const p = await ig(`${uid}/media_publish`, { creation_id: c.id });
      if (!p.id) throw new Error(JSON.stringify(p));
      postId = p.id;
    }

    // Cleanup tmp folder
    if (tmpId) {
      const dir = path.join(__dirname, '../public/uploads/tmp', tmpId.replace(/[^a-zA-Z0-9]/g, ''));
      try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
    }

    res.json({ success: true, postId });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ── Comments ──────────────────────────────────────────────────────────────────

// GET /api/ig/comments/:mediaId — list comments on a post
router.get('/ig/comments/:mediaId', requireUser, async (req, res) => {
  const token = req.user.ig_token;
  if (!token) return res.status(400).json({ error: 'Instagram not connected' });
  const { mediaId } = req.params;
  const safeId = mediaId.replace(/[^0-9_]/g, '');
  try {
    const r = await fetch(
      `https://graph.instagram.com/v21.0/${safeId}/comments?fields=id,text,username,timestamp,replies{id,text,username,timestamp}&access_token=${token}`
    );
    res.json(await r.json());
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// POST /api/ig/comments/:commentId/reply — reply to a comment
router.post('/ig/comments/:commentId/reply', requireUser, async (req, res) => {
  const token = req.user.ig_token;
  if (!token) return res.status(400).json({ error: 'Instagram not connected' });
  const { message } = req.body;
  if (!message) return res.status(400).json({ error: 'Missing message' });
  const { commentId } = req.params;
  const safeId = commentId.replace(/[^0-9_]/g, '');
  try {
    const r = await fetch(`https://graph.instagram.com/v21.0/${safeId}/replies`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body:    new URLSearchParams({ message, access_token: token })
    });
    res.json(await r.json());
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// DELETE /api/ig/comments/:commentId — hide a comment
router.delete('/ig/comments/:commentId', requireUser, async (req, res) => {
  const token = req.user.ig_token;
  if (!token) return res.status(400).json({ error: 'Instagram not connected' });
  const safeId = req.params.commentId.replace(/[^0-9_]/g, '');
  try {
    const r = await fetch(`https://graph.instagram.com/v21.0/${safeId}`, {
      method:  'DELETE',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body:    new URLSearchParams({ access_token: token })
    });
    res.json(await r.json());
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// ── Messaging ─────────────────────────────────────────────────────────────────

// GET /api/ig/conversations — list DM conversations
router.get('/ig/conversations', requireUser, async (req, res) => {
  const token = req.user.ig_token;
  if (!token) return res.status(400).json({ error: 'Instagram not connected' });
  try {
    const meRes = await fetch(`https://graph.instagram.com/v21.0/me?fields=id&access_token=${token}`);
    const { id: uid } = await meRes.json();
    const r = await fetch(
      `https://graph.instagram.com/v21.0/${uid}/conversations?platform=instagram&fields=id,participants,updated_time,messages{id,message,from,created_time}&access_token=${token}`
    );
    res.json(await r.json());
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// POST /api/ig/messages/send — send a DM
router.post('/ig/messages/send', requireUser, async (req, res) => {
  const token = req.user.ig_token;
  if (!token) return res.status(400).json({ error: 'Instagram not connected' });
  const { recipientId, message } = req.body;
  if (!recipientId || !message) return res.status(400).json({ error: 'Missing recipientId or message' });
  try {
    const meRes = await fetch(`https://graph.instagram.com/v21.0/me?fields=id&access_token=${token}`);
    const { id: uid } = await meRes.json();
    const r = await fetch(`https://graph.instagram.com/v21.0/${uid}/messages`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({
        recipient: { id: recipientId },
        message:   { text: message },
        access_token: token
      })
    });
    res.json(await r.json());
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// ── AI reply suggestion (Google Reviews, etc.) ────────────────────────────────
router.post('/ai-reply', requireUser, async (req, res) => {
  const key = process.env.DEEPSEEK_API_KEY;
  if (!key) return res.status(503).json({ error: 'AI not configured' });
  const { comment, stars } = req.body;
  try {
    const r = await fetch('https://api.deepseek.com/chat/completions', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
      body: JSON.stringify({
        model: 'deepseek-chat',
        messages: [
          { role: 'system', content: 'You are a professional business owner replying to Google Reviews. Write a warm, genuine, concise reply (2-4 sentences). No emojis. Match the language of the review. Return only the reply text, nothing else.' },
          { role: 'user', content: `Review (${stars}): "${comment || 'No comment provided.'}"` }
        ]
      })
    });
    const data = await r.json();
    const reply = data.choices?.[0]?.message?.content?.trim() || '';
    res.json({ reply });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// ── AI slide generation (optional DeepSeek) ───────────────────────────────────
router.post('/ai/generate', requireUser, async (req, res) => {
  const key = process.env.DEEPSEEK_API_KEY;
  if (!key) return res.status(503).json({ error: 'AI not configured' });
  const { prompt } = req.body;
  try {
    const r = await fetch('https://api.deepseek.com/chat/completions', {
      method:  'POST',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
      body: JSON.stringify({
        model: 'deepseek-chat',
        messages: [
          { role: 'system', content: 'You are a senior art director specializing in Instagram carousels. Return ONLY valid JSON: {"type":"single"|"carousel","slides":[{"html":"<div...>","caption":"..."}]}' },
          { role: 'user', content: prompt }
        ]
      })
    });
    const data = await r.json();
    const text = data.choices?.[0]?.message?.content || '';
    const json = JSON.parse(text.replace(/```json?|```/g, '').trim());
    res.json(json);
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// ── Carousel server storage ───────────────────────────────────────────
const WORKS_DIR = path.join(__dirname, '../data/works');

// GET /api/works — index (meta only, no slide HTML)
router.get('/works', requireUser, (req, res) => {
  const indexFile = path.join(WORKS_DIR, String(req.user.id), 'index.json');
  try {
    res.json(fs.existsSync(indexFile) ? JSON.parse(fs.readFileSync(indexFile, 'utf8')) : []);
  } catch(e) { res.json([]); }
});

// GET /api/works/:id — full work data
router.get('/works/:id', requireUser, (req, res) => {
  const safeId = req.params.id.replace(/[^a-zA-Z0-9]/g, '');
  const file = path.join(WORKS_DIR, String(req.user.id), safeId + '.json');
  try {
    res.json(JSON.parse(fs.readFileSync(file, 'utf8')));
  } catch(e) { res.status(404).json({ error: 'Not found' }); }
});

// POST /api/works — create or update
router.post('/works', requireUser, (req, res) => {
  try {
    const { id, label, count, bg_sample, created_at, data, chatHistory, undoStack } = req.body;
    const workId = String(id || Date.now());
    const dir    = path.join(WORKS_DIR, String(req.user.id));
    fs.mkdirSync(dir, { recursive: true });

    // Extract data: URLs → real image files → public URLs
    const pubDir = path.join(__dirname, '../public/uploads/users', String(req.user.id));
    fs.mkdirSync(pubDir, { recursive: true });
    const base = (req.secure ? 'https' : 'http') + '://' + req.get('host');

    function saveBlob(dataUrl, suffix) {
      const fname = Date.now() + '-' + Math.random().toString(36).slice(2) + suffix + '.jpg';
      const buf   = Buffer.from(dataUrl.replace(/^data:image\/\w+;base64,/, ''), 'base64');
      fs.writeFileSync(path.join(pubDir, fname), buf);
      return `${base}/uploads/users/${req.user.id}/${fname}`;
    }

    const proc = JSON.parse(JSON.stringify(data || {}));
    (proc.slides || []).forEach(s => {
      if (s.bg_image && s.bg_image.startsWith('data:'))
        s.bg_image = saveBlob(s.bg_image, '-bg');
      (s.overlay_images || []).forEach(img => {
        if (img.url && img.url.startsWith('data:'))
          img.url = saveBlob(img.url, '-oi');
      });
    });

    // Save work file (data + chat + undo)
    fs.writeFileSync(
      path.join(dir, workId + '.json'),
      JSON.stringify({ data: proc, chatHistory, undoStack })
    );

    // Update index
    const indexFile = path.join(dir, 'index.json');
    let index = [];
    try { index = JSON.parse(fs.readFileSync(indexFile, 'utf8')); } catch(e) {}
    const i = index.findIndex(w => w.id === workId);
    const meta = { id: workId, label: label || 'Untitled', count: count || 0,
                   bg_sample: bg_sample || '#1a1a1a',
                   created_at: created_at || new Date().toISOString() };
    if (i >= 0) index[i] = meta; else index.unshift(meta);
    if (index.length > 50) index.length = 50;
    fs.writeFileSync(indexFile, JSON.stringify(index));

    res.json({ success: true, id: workId, data: proc });
  } catch(e) { res.status(500).json({ error: e.message }); }
});

// DELETE /api/works/:id
router.delete('/works/:id', requireUser, (req, res) => {
  try {
    const safeId    = req.params.id.replace(/[^a-zA-Z0-9]/g, '');
    const dir       = path.join(WORKS_DIR, String(req.user.id));
    const indexFile = path.join(dir, 'index.json');
    try { fs.unlinkSync(path.join(dir, safeId + '.json')); } catch(e) {}
    try {
      let index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
      fs.writeFileSync(indexFile, JSON.stringify(index.filter(w => w.id !== safeId)));
    } catch(e) {}
    res.json({ success: true });
  } catch(e) { res.status(500).json({ error: e.message }); }
});

module.exports = router;
