//notes - works to get excel downloaded bypass downloader with "false"
// excelsync.js
import fs from "fs";
import axios from "axios";
import qs from "qs";
import * as XLSX from "xlsx";
import dotenv from "dotenv";
import { serve } from "bun";
import PocketBase from "pocketbase"

dotenv.config();

// -----------------------------
// CONFIG
// -----------------------------
const CONFIG = JSON.parse(fs.readFileSync("config.json", "utf8"));
const tenantId = CONFIG.tenantId;
const clientId = CONFIG.clientId;
const sharepointFileUrl = CONFIG.sharepointFileUrl; // production Excel file URL
const pbUrl = CONFIG.pbUrl;
const pbCollection = CONFIG.pbCollection || "Job_Info_TestEnv";
const pbEmail = CONFIG.pbEmail;

const clientSecret = process.env.CLIENT_SECRET;
const pbPassword = process.env.PB_PASSWORD;

if (!tenantId || !clientId || !clientSecret || !sharepointFileUrl || !pbUrl || !pbEmail || !pbPassword) {
  console.error("❌ Missing required config/env values");
  process.exit(1);
}
console.log({
  tenantId,
  clientId,
  clientSecret: clientSecret ? clientSecret.slice(0,4) + "..." : null,
  sharepointFileUrl,
  pbUrl,
  pbEmail,
  pbPassword: pbPassword ? "***" : null
});
// -----------------------------
// LOGGING
// -----------------------------
function log(message) {
  console.log(message);
  fs.appendFileSync("src/excelsync.log", message + "\n");
}

// -----------------------------
// GRAPH TOKEN
// -----------------------------
async function getGraphToken() {
  const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
  const body = qs.stringify({
    client_id: clientId,
    client_secret: clientSecret,
    scope: "https://graph.microsoft.com/.default",
    grant_type: "client_credentials",
  });
  const res = await axios.post(tokenUrl, body, {
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
  });
  log("✅ Graph token acquired");
  return res.data.access_token;
}

// -----------------------------
// GRAPH DOWNLOADER HELPER
// -----------------------------
async function downloadExcelFromGraph(graphToken, driveId, itemId) {
  const url = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/content`;
  const res = await axios.get(url, {
    headers: { Authorization: `Bearer ${graphToken}` },
    responseType: "arraybuffer",   // important: get raw binary
  });
  return Buffer.from(res.data);    // convert to Buffer for XLSX
}

// -----------------------------
// POCKETBASE LOGIN
// -----------------------------
async function getPocketBaseClient() {
  const pb = new PocketBase(pbUrl);
  await pb.collection("users").authWithPassword(pbEmail, pbPassword);
  log("✅ PocketBase token acquired");
  return pb;
}

// -----------------------------
// EXCEL PARSER
// -----------------------------
const HEADER_ROW_INDEX = 2;       // Row 3
const DATA_START_ROW_INDEX = 3;   // Row 4

function parseExcelFile(input, headerMap) {
  let data;
  // In case of data from graph
  if (Buffer.isBuffer(input)) {
    data = input;
    log(`Reading Excel file from Graph buffer`);
  // In case of file path (Not used at this time)
  } else {
    data = fs.readFileSync(input);
    log(`Reading Excel file: ${input}`);
  }

  const workbook = XLSX.read(data, { type: "buffer", cellDates: true });
  const sheet = workbook.Sheets["Job Sheet"]; // explicitly use Job Sheet
  const range = XLSX.utils.decode_range(sheet["!ref"]);
  const merges = sheet["!merges"] || [];

  const getValue = (r, c) => {
    const addr = XLSX.utils.encode_cell({ r, c });
    const cell = sheet[addr];
    if (cell) return cell.v ?? cell.w ?? null;
    for (const m of merges) {
      if (m.s.r <= r && r <= m.e.r && m.s.c <= c && c <= m.e.c) {
        const master = sheet[XLSX.utils.encode_cell({ r: m.s.r, c: m.s.c })];
        return master ? (master.v ?? master.w ?? null) : null;
      }
    }
    return null;
  };

  // Read headers
  const rawHeaders = [];
  for (let c = range.s.c; c <= range.e.c; c++) {
    let h = getValue(HEADER_ROW_INDEX, c);
    rawHeaders.push(h ? String(h).trim() : "");
  }
  log(`Found ${rawHeaders.length} headers in Row 3`);
  log(`Headers: ${rawHeaders.join(" | ")}`);
  // Compare Excel headers against HEADER_MAP
  const mappedHeaders = [];
  const unmappedHeaders = [];
  for (const h of rawHeaders) {
    if (headerMap[h]) {
      mappedHeaders.push([h, headerMap[h]]);
    } else {
      unmappedHeaders.push([h, ""]);
    }
  }

// Output as CSV-style rows
console.log("\n📑 Header mapping preview (CSV style):");
console.log("ExcelHeader,PocketBaseField");
for (const [excel, pb] of mappedHeaders) {
  console.log(`${excel},${pb}`);
}
for (const [excel, pb] of unmappedHeaders) {
  console.log(`${excel},${pb}`);
}

  // Parse data rows
  const rows = [];
  for (let r = DATA_START_ROW_INDEX; r <= range.e.r; r++) {
    const row = {};
    let hasData = false;
    for (let c = range.s.c; c <= range.e.c; c++) {
      const excelHeader = rawHeaders[c - range.s.c];
      const pbField = headerMap[excelHeader];
      if (!pbField) continue;
      let val = getValue(r, c);
      if (val !== null && val !== undefined) {
        const s = String(val).trim();
        if (s === "" || /^n\/?a$/i.test(s) || s === "#N/A") val = null;
      }
      row[pbField] = val;
      if (val !== null && val !== "") hasData = true;
    }
    if (hasData) rows.push(row);
  }

  log(`Loaded ${rows.length} real jobs`);
  if (rows[0]) {
    log(`First job preview: Job_Number=${rows[0].Job_Number}, pb_id=${rows[0].id}`);

  }
  return rows;
}

// -----------------------------
// HEADER MAP
// -----------------------------
const HEADER_MAP = {
  "Job_Number": "Job_Number",
  "Job_Full_Name": "Job_Full_Name",
  "Job_Name": "Job_Name",
  "Job Address": "Job_Address",
  "Job Type": "Job_Type",
  "Flag": "Flag",
  "Tax_Exempt": "Tax_Exempt",
  "Job Status": "Job_Status",
  "Job Division": "Job_Division",
  "Estimator": "Estimator",
  "Office Rep": "Office_Rep",
  "Manager": "Project_Manager",
  "Company/Client": "Company_Client",
  "Contact Person": "Contact_Person",
  "Phone Number": "Phone_Number",
  "Email": "Email",
  "Due Date": "Due_Date",
  "Due Date Source": "Due_Date_Source",
  "Due Date Counter": "Due_Date_Counter",
  "Due Time": "Due_Time",
  "Tax_Exempt_Docs_Uploaded": "Tax_Exempt_Docs_Uploaded",
  "Docs Uploaded": "Docs_Uploaded",
  "QB Created": "QB_Created",
  "Added to Calendar": "Added_To_Calendar",
  "PSwift Uploaded": "PSwift_Uploaded",
  "Voxer Created": "Voxer_Created",
  "Estimate Reviewed": "Estimate_Reviewed",
  "Estimate Draft": "Estimate_Draft",
  "Estimate Approved": "Estimate_Approved",
  "Start Date": "Start_Date",
  "Schedule_Confidence": "Schedule_Confidence",
  "Notes": "Notes",
  "Job QB Link": "Job_QB_Link",
  "Voxer Link": "Voxer_Link",
  "Job_Codes": "Job_Codes",
  "Submission Date": "Submission_Date",
  "Active": "Active",
  "Job Folder Link": "Job_Folder_Link",
  "pb_id": "id"
};

// -----------------------------
// MAIN
// -----------------------------
let parsedRows = [];
(async () => {
  try {
    log("🔵 Starting Excel Sync…");

    const graphToken = await getGraphToken();
    const pb = await getPocketBaseClient();
    // These can be update if the Excel File changes
    const driveId = "b!9YOqqr2xM0G2DFBwMEJYY4UzQgocFddEtpLYWuS9_AtkCKl2q8yjQJteQ7Ti4QSx";
    const itemId = "01SPNXLDSQTMG4F6BDANCLJU2FZKEDQLVU";
    const excelBuffer = await downloadExcelFromGraph(graphToken, driveId, itemId);
    parsedRows = parseExcelFile(excelBuffer, HEADER_MAP);

    // Run enhanced test (safe, no writes)
    runEnhancedTest(parsedRows, graphToken, pb);
    // At this stage you have:
    // - graphToken (for SharePoint updates later)
    // - pb client (for PB updates)
    // - rows[] mapped to PB fields

    // Sync to PocketBase
    //await syncRowsToPocketBase(parsedRows, pb);

    log("✅ First part complete: tokens + Excel parsing ready");
  } catch (err) {
    log("❌ Sync failed: " + (err.message || err));
    process.exit(1);
  }
})();
// -----------------------------
// ENHANCED TEST HARNESS
// -----------------------------
function runEnhancedTest(rows, graphToken, pb) {
  log("🔎 Running enhanced test (no writes)…");

  // 1. Verify tokens
  log(`Graph token length: ${graphToken.length}`);
  log(`PocketBase token length: ${pb.authStore.token.length}`);

  // 2. Verify Excel parsing
  log(`Parsed ${rows.length} rows from Excel`);
  if (rows.length > 0) {
    const sample = rows[0];
    log("Sample row preview:");
    log(`  Job_Number: ${sample.Job_Number}`);
    log(`  Job_Full_Name: ${sample.Job_Full_Name}`);
    log(`  Active: ${sample.Active}`);
    log(`  pb_id: ${sample.id}`);
    log(`  Notes: ${sample.Notes}`);
  }

  // 3. Verify header mapping
  const mappedFields = Object.values(HEADER_MAP);
  const sampleKeys = Object.keys(rows[0] || {});
  const missingFields = mappedFields.filter(f => !sampleKeys.includes(f));
  if (missingFields.length > 0) {
    log(`⚠️ Missing mapped fields in sample row: ${missingFields.join(", ")}`);
  } else {
    log("✅ All mapped fields present in sample row");
  }

  log("✅ Enhanced test complete — no writes performed");
}



// -------------------------
// Export to xlsx - Excel
// ---------------------------

// Toggle this flag to enable/disable the export server
const ENABLE_EXPORT = true;   // set to false to bypass download

function exportExcelforDL(parsedRows) {
  if (!parsedRows || parsedRows.length === 0) {
    throw new Error("⚠️ No rows to export");
  }

  const worksheet = XLSX.utils.json_to_sheet(parsedRows);

  // Ensure Job_Number is the first column
  const cols = Object.keys(parsedRows[0] || {});
  if (cols.includes("Job_Number")) {
    const ordered = ["Job_Number", ...cols.filter(c => c !== "Job_Number")];
    const remapped = parsedRows.map(r =>
      ordered.reduce((acc, key) => { acc[key] = r[key]; return acc; }, {})
    );
    const ws2 = XLSX.utils.json_to_sheet(remapped);
    Object.assign(worksheet, ws2);
  }

  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, worksheet, "ParsedData");

  return XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
}

if (ENABLE_EXPORT) {
  const PORT = 3000;
  serve({
    port: PORT,
    fetch(req) {
      const url = new URL(req.url);

      if (url.pathname === "/") {
        const html = `
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>ExcelSync Export</title></head>
<body>
  <h1>ExcelSync Export</h1>
  <p>Click below to download the parsed Excel file:</p>
  <button onclick="window.location.href='/download'">Download ParsedOutput.xlsx</button>
</body>
</html>`;
        return new Response(html, { headers: { "Content-Type": "text/html" } });
      }

      if (url.pathname === "/download") {
        try {
          const buffer = exportExcelforDL(parsedRows);
          return new Response(buffer, {
            headers: {
              "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              "Content-Disposition": "attachment; filename=ParsedOutput.xlsx"
            }
          });
        } catch (err) {
          return new Response("❌ Failed to generate Excel file: " + err.message, { status: 500 });
        }
      }

      return new Response("Visit / to open the export page.");
    }
  });

  log(`🚀 ExcelSync export server running at http://localhost:${PORT}/`);
  log(`👉 Open this URL in your browser to verify and download the parsed Excel file.`);
} else {
  log("⏭️ Export server bypassed (ENABLE_EXPORT=false)");
}


// -----------------------------
// FINAL SYNC FUNCTION
// -----------------------------

