// server.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const { log } = require('./logger');

const auth = require('./auth');
const manageBrands = require('./routes/manageBrands');
const manageProducts = require('./routes/manageProducts');
const managepricing = require('./routes/managePricing');
const adminPanel = require('./routes/adminPanel');

const privacyLawWebhooks = require('./routes/privacyLawWebhooks');
const { getToken, saveToken, listTokens } = require('./tokenStore');
const { listJobs, getJob, cancelJob, getLatestJobForShop } = require('./jobStore');
const { isShopAllowed } = require('./freeAccessStore');
const { addMessage: addChatMessage, readChat } = require('./chatStore');
const { createFulfillmentService } = require('./fulfillmentService');

const app = express();
const PORT = process.env.PORT || 3002;

// 0) CORS (safe before everything)
app.use(cors());

// Admin panel (mounted before JSON body parser — it handles its own parsing)
app.use('/d4a-admin', adminPanel);

// Public free-access check used by the Shopify frontend loaders
app.get('/free-access/:shop', (req, res) => {
  const shop = decodeURIComponent(req.params.shop || '').toLowerCase().trim();
  const allowed = isShopAllowed(shop);
  res.json({ shop, allowed });
});

// ── BACKFILL: Re-run fulfillment service + locationId for shops that have null ─
// Hit: GET /admin/fix-location          → runs all shops with null locationId
// Hit: GET /admin/fix-location/:shop    → runs one specific shop
async function runFixLocation(req, res) {
  const stores = listTokens();
  const targetShop = req.params.shop ? decodeURIComponent(req.params.shop).toLowerCase().trim() : null;

  const toFix = Object.entries(stores).filter(([shop, record]) => {
    if (targetShop) return shop === targetShop;
    return !record.locationId;
  });

  if (toFix.length === 0) {
    return res.json({ message: 'No shops need fixing', checked: Object.keys(stores).length });
  }

  const results = [];
  for (const [shop, record] of toFix) {
    if (!record.accessToken) {
      results.push({ shop, status: 'skipped', reason: 'no accessToken' });
      continue;
    }
    try {
      log(shop, `🔧 [BACKFILL] Starting fulfillment setup — token: ${record.accessToken?.slice(0,10)}...`);
      const { fulfillmentService, locationId } = await createFulfillmentService(shop, record.accessToken);
      log(shop, `🔧 [BACKFILL] createFulfillmentService returned → fulfillmentService=${JSON.stringify(fulfillmentService?.id || null)}, locationId=${locationId}`);

      if (!locationId) {
        log(shop, `❌ [BACKFILL] locationId is STILL null after createFulfillmentService — token is likely expired/revoked for this shop`);
        results.push({ shop, status: 'failed', reason: 'locationId still null — token likely revoked, shop must reinstall', locationId: null });
        continue;
      }

      saveToken(shop, record.accessToken, record.scope, fulfillmentService, locationId);
      results.push({ shop, status: 'fixed', locationId, fulfillmentServiceId: fulfillmentService?.id || null });
      log(shop, `✅ [BACKFILL] SUCCESS — locationId=${locationId}`);
    } catch (err) {
      results.push({ shop, status: 'error', error: err.message });
      log(shop, `❌ [BACKFILL] Exception: ${err.message}`);
    }
  }

  res.json({ fixed: results.filter(r => r.status === 'fixed').length, total: toFix.length, results });
}

app.get('/admin/fix-location', runFixLocation);
app.get('/admin/fix-location/:shop', runFixLocation);

// Debug: test if a shop's stored token is still valid
app.get('/admin/check-token/:shop', async (req, res) => {
  const shop = decodeURIComponent(req.params.shop).toLowerCase().trim();
  const record = getToken(shop);
  if (!record) return res.json({ shop, status: 'not_found' });
  if (!record.accessToken) return res.json({ shop, status: 'no_token' });
  try {
    const axios = require('axios');
    const resp = await axios.get(`https://${shop}/admin/api/2025-10/shop.json`, {
      headers: { 'X-Shopify-Access-Token': record.accessToken },
    });
    res.json({ shop, status: 'token_valid', shopName: resp.data?.shop?.name, locationId: record.locationId });
  } catch (err) {
    const statusCode = err.response?.status;
    res.json({ shop, status: 'token_invalid', httpStatus: statusCode, error: err.response?.data || err.message, locationId: record.locationId });
  }
});

// ── PUBLIC CHAT ENDPOINTS (for customer widget) ──────────────────────────────
// widget.js MUST be before /chat/:shop — otherwise Express treats "widget.js" as the :shop param
app.get('/chat/widget.js', (req, res) => {
  const shop = (req.query.shop || '').toLowerCase().trim();
  const backendUrl = 'https://backend.data4autos.com';
  res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
  res.setHeader('Cache-Control', 'public, max-age=300');
  res.send(`(function(){
  if(!window.__d4aChat) window.__d4aChat={};
  var SHOP='${shop}', BASE='${backendUrl}';
  var VID=localStorage.getItem('d4a_vid')||'v'+(Math.random().toString(36).slice(2));
  localStorage.setItem('d4a_vid',VID);
  var css=\`
    #d4a-fab{position:fixed;bottom:24px;right:24px;width:56px;height:56px;background:linear-gradient(135deg,#2563eb,#4f46e5);border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 20px rgba(37,99,235,0.4);z-index:99999;transition:transform .15s;}
    #d4a-fab:hover{transform:scale(1.08);}
    #d4a-fab svg{width:26px;height:26px;fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
    #d4a-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;background:#ef4444;border-radius:9px;font-size:11px;font-weight:700;color:#fff;display:none;align-items:center;justify-content:center;padding:0 4px;border:2px solid #fff;}
    #d4a-win{position:fixed;bottom:92px;right:24px;width:360px;max-height:520px;background:#fff;border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,0.18);z-index:99998;display:none;flex-direction:column;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Inter',sans-serif;}
    #d4a-hdr{background:linear-gradient(135deg,#2563eb,#4f46e5);padding:16px 18px;display:flex;align-items:center;gap:10px;}
    #d4a-hdr-icon{width:36px;height:36px;background:rgba(255,255,255,.2);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px;}
    #d4a-hdr-title{font-size:14px;font-weight:700;color:#fff;}
    #d4a-hdr-sub{font-size:12px;color:rgba(255,255,255,.75);margin-top:1px;}
    #d4a-msgs{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;background:#f8fafc;min-height:200px;max-height:340px;}
    .d4a-bub{max-width:80%;padding:9px 13px;font-size:13px;line-height:1.5;border-radius:12px;}
    .d4a-bub.admin{background:#fff;color:#0f172a;border:1px solid #e2e8f0;align-self:flex-start;border-radius:12px 12px 12px 3px;}
    .d4a-bub.customer{background:#2563eb;color:#fff;align-self:flex-end;border-radius:12px 12px 3px 12px;}
    .d4a-time{font-size:10px;color:#94a3b8;margin-top:3px;}
    .d4a-time.r{text-align:right;}
    #d4a-inp-row{padding:12px;border-top:1px solid #e2e8f0;display:flex;gap:8px;background:#fff;}
    #d4a-inp{flex:1;border:1.5px solid #e2e8f0;border-radius:8px;padding:9px 12px;font-size:13px;font-family:inherit;outline:none;resize:none;}
    #d4a-inp:focus{border-color:#2563eb;}
    #d4a-send{background:#2563eb;border:none;border-radius:8px;color:#fff;padding:9px 16px;font-size:13px;font-weight:700;cursor:pointer;white-space:nowrap;}
    #d4a-send:hover{background:#1d4ed8;}
  \`;
  var el=document.createElement('style');el.textContent=css;document.head.appendChild(el);
  var fab=document.createElement('div');fab.id='d4a-fab';
  fab.innerHTML='<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg><div id="d4a-badge"></div>';
  document.body.appendChild(fab);
  var win=document.createElement('div');win.id='d4a-win';
  win.innerHTML='<div id="d4a-hdr"><div id="d4a-hdr-icon">💬</div><div><div id="d4a-hdr-title">Chat with us</div><div id="d4a-hdr-sub">We typically reply within a few hours</div></div></div><div id="d4a-msgs"><div style="text-align:center;color:#94a3b8;font-size:13px;padding-top:20px;">Send us a message and we\'ll get back to you!</div></div><div id="d4a-inp-row"><textarea id="d4a-inp" rows="1" placeholder="Type a message…" onkeydown="if(event.key===\'Enter\'&&!event.shiftKey){event.preventDefault();window.__d4aChat.send();}"></textarea><button id="d4a-send" onclick="window.__d4aChat.send()">Send</button></div>';
  document.body.appendChild(win);
  var open=false,unread=0,lastTs='',polling=null;
  fab.onclick=function(){
    open=!open;win.style.display=open?'flex':'none';
    if(open){unread=0;document.getElementById('d4a-badge').style.display='none';loadMsgs(true);}
    else if(polling){clearInterval(polling);polling=null;}
    if(open&&!polling) polling=setInterval(function(){loadMsgs(false);},3000);
  };
  function loadMsgs(scroll){
    fetch(BASE+'/chat/'+encodeURIComponent(SHOP)+'?vid='+VID).then(r=>r.json()).then(function(d){
      var msgs=d.messages||[];var el=document.getElementById('d4a-msgs');
      if(!msgs.length) return;
      var latest=msgs[msgs.length-1]?.timestamp||'';
      var isNew=latest&&latest!==lastTs;lastTs=latest;
      if(!isNew&&!scroll) return;
      el.innerHTML=msgs.map(function(m){
        var isAdmin=m.from==='admin';
        var t=new Date(m.timestamp).toLocaleTimeString(undefined,{hour:'2-digit',minute:'2-digit'});
        return '<div style="display:flex;flex-direction:column;align-items:'+(isAdmin?'flex-start':'flex-end')+'"><div class="d4a-bub '+(isAdmin?'admin':'customer')+'">'+esc(m.text)+'</div><div class="d4a-time'+(isAdmin?'':' r')+'">'+t+'</div></div>';
      }).join('');
      if(scroll||el.scrollHeight-el.scrollTop-el.clientHeight<80) el.scrollTop=el.scrollHeight;
      if(!open&&isNew&&msgs[msgs.length-1]?.from==='admin'){
        unread++;var b=document.getElementById('d4a-badge');b.textContent=unread>9?'9+':unread;b.style.display='flex';
      }
    }).catch(function(){});
  }
  window.__d4aChat.send=function(){
    var inp=document.getElementById('d4a-inp');var text=inp.value.trim();if(!text) return;
    inp.value='';
    fetch(BASE+'/chat/'+encodeURIComponent(SHOP),{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text:text,visitorId:VID})}).then(function(){loadMsgs(true);});
  };
  function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
  loadMsgs(true);
})();`);
});

// One-time backfill: register chat widget ScriptTag on all already-installed shops
// Hit: GET /chat/backfill-scripttags  (admin use only — no sensitive data exposed)
app.get('/chat/backfill-scripttags', async (req, res) => {
  const { getToken, listTokens } = require('./tokenStore');
  const axios = require('axios');
  const stores = listTokens();
  const results = [];
  for (const [shop, record] of Object.entries(stores)) {
    if (!record.accessToken) { results.push({ shop, status: 'no-token' }); continue; }
    const WIDGET_SRC = `https://backend.data4autos.com/chat/widget.js?shop=${encodeURIComponent(shop)}`;
    const headers = { 'X-Shopify-Access-Token': record.accessToken, 'Content-Type': 'application/json' };
    try {
      const ex = await axios.get(`https://${shop}/admin/api/2025-10/script_tags.json?src=${encodeURIComponent(WIDGET_SRC)}`, { headers });
      if (ex.data?.script_tags?.length > 0) { results.push({ shop, status: 'already-exists' }); continue; }
      await axios.post(`https://${shop}/admin/api/2025-10/script_tags.json`, { script_tag: { event: 'onload', src: WIDGET_SRC } }, { headers });
      results.push({ shop, status: 'registered' });
    } catch (e) {
      results.push({ shop, status: 'error', error: e.response?.data || e.message });
    }
  }
  res.json({ results });
});

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

// Top-level job endpoints (mirrors manageproducts/jobs/* but at /jobs/*)
app.get('/jobs', (req, res) => {
  const shop = req.query.shop || null;
  res.json({ jobs: listJobs(shop) });
});

app.get('/jobs/:jobId', (req, res) => {
  const job = getJob(req.params.jobId);
  if (!job) return res.status(404).json({ error: 'Job not found' });
  res.json(job);
});

app.post('/jobs/:jobId/cancel', (req, res) => {
  const job = cancelJob(req.params.jobId);
  if (!job) return res.status(404).json({ error: 'Job not found' });
  res.json({ ok: true, job });
});

app.get('/shops', (req, res) => {
  try {
    const store = listTokens();
    const shops = Object.keys(store).map(shop => ({
      shop,
      savedAt: store[shop].savedAt,
      hasToken: !!store[shop].accessToken,
      hasLocation: !!store[shop].locationId,
      hasFulfillment: !!store[shop].fulfillmentService,
    }));
    res.json({ shops });
  } catch {
    res.json({ shops: [] });
  }
});

app.get("/checkisshopdataexists/:shop", (req, res) => {
  const shop = req.params.shop;
  console.log("GET /checkisshopdataexists:", shop);

  const tokenRecord = getToken(shop);

  if (!tokenRecord) {
    return res.json({ status: 0, message: "Shop not found" });
  }

  // Expected fields
  const expectedFields = [
    "accessToken",
    "scope",
    "savedAt",
    "locationId",
    "fulfillmentService"
  ];

  const result = {};
  expectedFields.forEach((field) => {
    result[field] = tokenRecord[field] ? "present" : "missing";
  });

  res.json({
    status: 1,
    shop,
    fields: result
  });
});



// 1) COMPLIANCE WEBHOOKS (raw body) — MUST be before any JSON body parser
app.use('/webhooks', privacyLawWebhooks);

// 2) OAuth / other routes
app.use('/', auth);

// 3) Body parsers for the rest of your app
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));

// 4) Chat message endpoints (need body parser — must be after it)
app.post('/chat/:shop', (req, res) => {
  const shop = decodeURIComponent(req.params.shop || '').toLowerCase().trim();
  const { text, visitorId } = req.body;
  if (!shop || !text) return res.status(400).json({ error: 'shop and text required' });
  const msg = addChatMessage(shop, 'customer', String(text).slice(0, 1000), visitorId || null);
  res.json({ ok: true, message: msg });
});

app.get('/chat/:shop', (req, res) => {
  const shop = decodeURIComponent(req.params.shop || '').toLowerCase().trim();
  if (!shop) return res.status(400).json({ error: 'shop required' });
  const chat = readChat(shop);
  res.json({ messages: chat.messages || [] });
});

// 5) Your other endpoints
app.post('/fulfillment', (req, res) => {
  console.log('POST /fulfillment:', req.body);
  res.sendStatus(200);
});

app.use('/managebrands', manageBrands);
app.use('/manageproducts', manageProducts);
app.use('/managepricing', managepricing);

const server = app.listen(PORT, () => {
  log('general', `🖥️ Server listening on port ${PORT}`);
  console.log(`Server running on https://backend.data4autos.com/`);
});

server.on('error', err => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} is already in use. Choose a different PORT or kill the process using it.`);
    process.exit(1);
  } else {
    console.error('Server error:', err);
    process.exit(1);
  }
});



// // server.js
// require('dotenv').config();
// const express = require('express');
// const { log } = require('./logger');

// // OAuth callback
// const auth = require('./auth');

// // Your job-routes
// const manageBrands = require('./routes/manageBrands');
// const manageProducts = require('./routes/manageProducts');
// const managepricing = require('./routes/managePricing');
// // const syncInventory   = require('./routes/syncInventory');
// // const syncCustomers   = require('./routes/syncCustomers');
// // // …etc, one per file in routes/

// const app = express();
// const PORT = process.env.PORT || 3002;
// const cors = require('cors');

// app.use(express.json({ limit: '10mb' }));
// app.use(express.urlencoded({ limit: '10mb', extended: true }));



// app.use(express.json());

// // 1) OAuth
// app.use('/', auth);
// app.use(cors());
// // 2) Job endpoints (manually mapped)
// app.post('/fulfillment', (req, res) => {
//   console.log('POST request received:', req.body); // Optional logging
//   res.sendStatus(200); // Sends 200 OK
// });

// app.use('/managebrands', manageBrands);
// app.use('/manageproducts', manageProducts);
// app.use('/managepricing', managepricing);
// const server = app.listen(PORT, () => {
//   log('general', `🖥️ Server listening on port ${PORT}`);
//   console.log(`Server running on https://backend.data4autos.com/`);
// });

// server.on('error', err => {
//   if (err.code === 'EADDRINUSE') {
//     console.error(`Port ${PORT} is already in use. Choose a different PORT or kill the process using it.`);
//     process.exit(1);
//   } else {
//     console.error('Server error:', err);
//     process.exit(1);
//   }
// });

// // app.use('/syncinventory',  syncInventory);
// // app.use('/synccustomers',  syncCustomers);
// // add more here as you create new files in routes/

// // app.listen(PORT, () => {
// //   log('general', `🖥️ Server listening on port ${PORT}`);
// //   console.log(`Server running on https://backend.dine360.ca/`);
// // });



// // // server.js
// // require('dotenv').config();
// // const express = require('express');
// // const auth    = require('./auth');
// // const { log } = require('./logger');

// // const app  = express();
// // const PORT = process.env.PORT || 3002;

// // // mount the auth routes
// // app.use('/', auth);

// // app.listen(PORT, () => {
// //   log('general', `🖥️ Server listening on port ${PORT}`);
// //   console.log(`Server running on http://localhost:${PORT}`);
// // });




// // const express = require('express');
// // const axios = require('axios');

// // const app = express();
// // const PORT = 3002;

// // // Replace these with your app's credentials
// // const CLIENT_ID = 'b7534c980967bad619cfdb9d3f837cfa';
// // const CLIENT_SECRET = 'ed6882a4fc5839df0677ad1bb3c92f2b';


// // app.get('/auth/callback', async (req, res) => {
// //   console.log('🔔 [Callback] Received OAuth callback');
// //   const { shop, code } = req.query;

// //   if (!shop || !code) {
// //     console.warn('⚠️ [Callback] Missing shop or code in query:', req.query);
// //     return res.status(400).send('Missing shop or code parameter.');
// //   }

// //   console.log(`🔍 [Callback] shop=${shop}, code=${code}`);

// //   try {
// //     console.log('🚀 [OAuth] Exchanging authorization code for access token...');

// //     const tokenResponse = await axios.post(
// //       `https://${shop}/admin/oauth/access_token`,
// //       {
// //         client_id: CLIENT_ID,
// //         client_secret: CLIENT_SECRET,
// //         code: code,
// //       },
// //       {
// //         headers: { 'Content-Type': 'application/json' },
// //       }
// //     );

// //     console.log('✅ [OAuth] Token endpoint responded:', tokenResponse.data);

// //     const { access_token, scope } = tokenResponse.data;
// //     console.log('🔑 [OAuth] Access Token:', access_token);
// //     console.log('📜 [OAuth] Granted Scopes:', scope);

// //     // TODO: Persist access_token securely in your database here
// //     console.log(`💾 [Store] Storing access token for shop ${shop} (simulate DB save)`);

// //     res.send('Access token received and logged. You can close this window.');
// //   } catch (error) {
// //     console.error(
// //       '❌ [OAuth] Error exchanging code for access token:',
// //       error.response?.data || error.message
// //     );
// //     res.status(500).send('Failed to get access token');
// //   }
// // });

// // app.listen(PORT, () => {
// //   console.log(`🖥️ [Server] Listening on http://localhost:${PORT}`);
// // });





// // // 🔔 [Callback] Received OAuth callback
// // // 🔍 [Callback] shop=veloxautomotive.myshopify.com, code=03f875ca02185dea8e60226c9263f3ba
// // // 🚀 [OAuth] Exchanging authorization code for access token...
// // // ✅ [OAuth] Token endpoint responded: {
// // //   access_token: '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',
// // //   scope: 'write_inventory,write_products,write_publications'
// // // }
// // // 🔑 [OAuth] Access Token: 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
// // // 📜 [OAuth] Granted Scopes: write_inventory,write_products,write_publications
// // // 💾 [Store] Storing access token for shop veloxautomotive.myshopify.com (simulate DB save)
