// /**
//  * buildValidateBody.js
//  * Purpose: Construct and validate the request body for bill validation API.
//  * Author: [Your Name]
//  * Experience: Senior Backend Engineer (20+ years)
//  */

// const { getBillerById } = require("../models/billerModel");
// const { safeParseJson } = require("../utils/billerUtils");
// const { validateFieldValue, isEmpty  } = require("../utils/validators");
// const dotenv = require("dotenv");
// dotenv.config();

// /**
//  * Custom error class for validation errors
//  * Helps keep all validation failures consistent for the frontend
//  */
// class ValidationError extends Error {
//   constructor(code, message) {
//     super(message);
//     this.code = code;
//     this.name = "ValidationError";
//   }
// }

// /**
//  * Utility to return standardized error response
//  */
// function buildErrorResponse(err) {
//   if (err instanceof ValidationError) {
//     return {
//       flow: "error",
//       error: {
//         code: err.code,
//         message: err.message,
//       },
//     };
//   }

//   // Catch-all for unexpected/unhandled errors
//   return {
//     flow: "error",
//     error: {
//       code: 5000,
//       message: err.message || "Internal server error",
//     },
//   };
// }

// /**
//  * Main function: Validates input body and builds request structure
//  */
// async function buildValidateBody(reqBody = {}) {
//   try {
//     // --- 🧩 STEP 1: Basic Request Body Validation ---
//     if (typeof reqBody !== "object" || Array.isArray(reqBody)) {
//       throw new ValidationError(4000, "Invalid or malformed request body");
//     }

//     const {
//       billerid,
//       authenticatorsFromUI = [],
//       additional_validation_details = [],
//       customer = {},
//       device = {},
//       payment_amount,
//     } = reqBody;

//     // --- 🧾 STEP 2: Biller Validation ---
//     if (!billerid) throw new ValidationError(4001, "Missing required field: billerid");

//     const biller = await getBillerById(billerid);
//     if (!biller) throw new ValidationError(4002, "Invalid or unknown billerid");

//     // Optional check for biller status
//     // if (biller.biller_status?.toUpperCase() !== "ACTIVE") {
//     //   throw new ValidationError(4003, "Biller is not active");
//     // }

//     // Skip validation flow if online validation is disabled
//     if ((biller.online_validation || "").toUpperCase() === "N") {
//       return { flow: "adhoc", reason: "online_validation=N", billerid };
//     }

//     // --- 🧠 STEP 2.5: Ensure all major objects exist ---
// if (!reqBody.authenticatorsFromUI || !Array.isArray(reqBody.authenticatorsFromUI)) {
//   throw new ValidationError(4009, "Missing or invalid object: authenticatorsFromUI");
// }

// if (!reqBody.customer || typeof reqBody.customer !== "object") {
//   throw new ValidationError(4010, "Missing or invalid object: customer");
// }

// if (!reqBody.device || typeof reqBody.device !== "object") {
//   throw new ValidationError(4011, "Missing or invalid object: device");
// }

// if (!Array.isArray(reqBody.additional_validation_details)) {
//   throw new ValidationError(4012, "additional_validation_details must be an array");
// }

//     // --- 🔐 STEP 3: Authenticators Validation ---
//     // const authRules = safeParseJson(biller.authenticators) || [];
//     // const finalAuthenticators = [];

//     // for (const rule of authRules) {
//     //   const ruleName = rule.parameter_name?.trim();
//     //   if (!ruleName) continue;

//     //   const ui = authenticatorsFromUI.find(
//     //     (a) => a.parameter_name?.trim().toLowerCase() === ruleName.toLowerCase()
//     //   );
//     //   const value = ui?.value;

//     //   // Validate against rule-level constraints
//     //   validateFieldValue(value, rule);

//     //   if (!isEmpty(value)) {
//     //     finalAuthenticators.push({ parameter_name: ruleName, value });
//     //   }
//     // }

//     // --- 🔐 STEP 3: Authenticators Validation ---
// const authRules = safeParseJson(biller.authenticators) || [];
// const finalAuthenticators = [];

// for (const rule of authRules) {
//   const ruleName = rule.parameter_name?.trim();
//   if (!ruleName) continue;

//   const ui = reqBody.authenticatorsFromUI.find(
//     (a) => a.parameter_name?.trim().toLowerCase() === ruleName.toLowerCase()
//   );

//   if (!ui) {
//     // If expected rule missing in request
//     throw new ValidationError(
//       4013,
//       `Missing expected authenticator: ${ruleName}`
//     );
//   }

//   const value = ui?.value;

//   // Validate against rule-level constraints
//   validateFieldValue(value, rule);

//   if (!isEmpty(value)) {
//     finalAuthenticators.push({ parameter_name: ruleName, value });
//   }
// }

// // Check for extra/unknown authenticators sent by frontend
// for (const uiAuth of reqBody.authenticatorsFromUI) {
//   const existsInRule = authRules.some(
//     (r) => r.parameter_name?.trim().toLowerCase() === uiAuth.parameter_name?.trim().toLowerCase()
//   );
//   if (!existsInRule) {
//     throw new ValidationError(
//       4014,
//       `Unknown authenticator parameter: ${uiAuth.parameter_name}`
//     );
//   }
// }


//     // --- 👤 STEP 4: Customer Validation ---
//     if (!customer.mobile) {
//       throw new ValidationError(4004, "Customer mobile number is mandatory");
//     }

//     // --- 💻 STEP 5: Device Validation ---
//     if (!device || typeof device !== "object" || Array.isArray(device)) {
//       throw new ValidationError(4005, "Device object is missing or invalid");
//     }

//     let initChannel = (device.init_channel || "").trim();
//     let agentid;
//     const deviceErrors = [];

//     // Determine device channel type
//     if (initChannel.toLowerCase() === "mobile") {
//       initChannel = "Mobile";
//       agentid = process.env.BBPS_MOBILE_AGENT_ID || "APP01";

//       const requiredMobileFields = ["ip", "imei", "os", "app"];
//       const missing = requiredMobileFields.filter((f) => !device[f]);
//       if (missing.length > 0) {
//         deviceErrors.push(`Missing Mobile fields: ${missing.join(", ")}`);
//       }
//     } else {
//       initChannel = "Internet";
//       agentid = process.env.BBPS_INTERNET_AGENT_ID || "WEB01";

//       const requiredWebFields = ["ip", "mac"];
//       const missing = requiredWebFields.filter((f) => !device[f]);
//       if (missing.length > 0) {
//         deviceErrors.push(`Missing Internet fields: ${missing.join(", ")}`);
//       }
//     }

//     if (deviceErrors.length > 0) {
//       throw new ValidationError(4006, deviceErrors.join("; "));
//     }

//     // --- 🧱 STEP 6: Construct Base Validation Body ---
//     if (!reqBody.customerid) {
//   throw new ValidationError(4008, "Missing required field: customerid");
// }
// let devicePayload = {
//   init_channel: initChannel,
//   ip: device.ip || "",
// };

// if (initChannel === "Internet") {
//   devicePayload.mac = device.mac || "";
// } else if (initChannel === "Mobile") {
//   devicePayload.imei = device.imei || "";
//   devicePayload.os = device.os || "";
//   devicePayload.app = device.app || "";
// }


//     const validateBody = {
//       customerid: reqBody.customerid,
//       billerid,
//       authenticators: finalAuthenticators,
//       currency: "356",
//       customer: {
//         firstname: customer.firstname || "",
//         lastname: customer.lastname || "",
//         mobile: customer.mobile || "",
//         email: customer.email || "",
//       },
//       metadata: {
//         agent: { agentid },
//         device: devicePayload,
//       },
//       risk: [
//         {
//           score_provider: process.env.BBPS_OU_ID || "DEFAULT",
//           score_value: "030",
//           score_type: "TXNRISK",
//         },
//         {
//           score_provider: "BBPS",
//           score_value: "030",
//           score_type: "TXNRISK",
//         },
//       ],
//     };

//     // --- 💰 STEP 7: Payment & Additional Validation ---
//     if ((biller.paymentamount_validation || "").toUpperCase() === "Y") {
//       if (!payment_amount) {
//         throw new ValidationError(4007, "Missing required field: payment_amount");
//       }

//       validateBody.payment_amount = payment_amount;

//       const addlRules = safeParseJson(biller.additional_validation_details);
//       if (Array.isArray(addlRules) && addlRules.length > 0) {
//         const addl = [];

//         for (const rule of addlRules) {
//           const ruleName = rule.parameter_name?.trim();
//           if (!ruleName) continue;

//           const ui = additional_validation_details.find(
//             (a) => a.parameter_name?.trim().toLowerCase() === ruleName.toLowerCase()
//           );
//           const value = ui?.value;

//           validateFieldValue(value, rule);
//           if (!isEmpty(value)) addl.push({ parameter_name: ruleName, value });
//         }

//         if (addl.length > 0) {
//           validateBody.additional_validation_details = addl;
//         }
//       }
//     }

//     // ✅ Everything validated successfully
//     return { flow: "validate", body: validateBody };
//   } catch (err) {
//     // Centralized error handling
//     return buildErrorResponse(err);
//   }
// }

// module.exports = { buildValidateBody };


// services/buildValidateBody.js

const buildValidateBody = async (reqBody) => {
  try {
    // --- 1️⃣ Basic required fields ---
    const requiredFields = ["customerid", "billerid", "authenticatorsFromUI"];
    for (const field of requiredFields) {
      if (!reqBody[field]) {
        return {
          flow: "error",
          error: {
            code: 4001,
            message: `Missing required field: ${field}`,
          },
        };
      }
    }

    // --- 2️⃣ Object name validation ---
    const allowedObjects = [
      "customerid",
      "billerid",
      "authenticatorsFromUI",
      "additional_validation_details",
      "payment_amount",
      "customer",
      "device",
    ];

    for (const key of Object.keys(reqBody)) {
      if (!allowedObjects.includes(key)) {
        return {
          flow: "error",
          error: {
            code: 4002,
            message: `Invalid object name in body: '${key}'. Please verify your request keys.`,
          },
        };
      }
    }

    // --- 3️⃣ Check authenticators array ---
    const authenticators = reqBody.authenticatorsFromUI;
    if (!Array.isArray(authenticators) || authenticators.length === 0) {
      return {
        flow: "error",
        error: {
          code: 4003,
          message: "At least one authenticator is required",
        },
      };
    }

    for (const auth of authenticators) {
      if (!auth.parameter_name || !auth.value) {
        return {
          flow: "error",
          error: {
            code: 4004,
            message: "Authenticator parameter_name and value are required",
          },
        };
      }
    }

    // --- 4️⃣ Validate customer section ---
    const customer = reqBody.customer || {};
    if (!customer.firstname || !customer.mobile) {
      return {
        flow: "error",
        error: {
          code: 4005,
          message: "Incomplete customer details provided",
        },
      };
    }

    // --- 5️⃣ Validate device info based on init_channel ---
    const device = reqBody.device || {};
    if (!device.init_channel) {
      return {
        flow: "error",
        error: {
          code: 4006,
          message: "Device init_channel is required",
        },
      };
    }

    if (device.init_channel === "Internet") {
      if (!device.ip || !device.mac) {
        return {
          flow: "error",
          error: {
            code: 4007,
            message: "Internet device must include ip and mac",
          },
        };
      }
    } else if (device.init_channel === "Mobile") {
      if (!device.imei || !device.os || !device.app) {
        return {
          flow: "error",
          error: {
            code: 4008,
            message: "Mobile device must include imei, os, and app",
          },
        };
      }
    }
    
    let agentId;
    // Choose agent ID dynamically based on init_channel
    if (device.init_channel === "Internet") {
        agentId = process.env.BBPS_INTERNET_AGENT_ID;
    } else {
        agentId = process.env.BBPS_MOBILE_AGENT_ID;
    } 

    // --- 6️⃣ Build the final payload ---
    const finalBody = {
      customerid: reqBody.customerid,
      billerid: reqBody.billerid,
      authenticators: authenticators,
      currency: "356",
      customer,
      metadata: {
        agent: {
          agentid: agentId,
        },
        device,
      },
      risk: [
        {
          score_provider: "BD01",
          score_value: "030",
          score_type: "TXNRISK",
        },
        {
          score_provider: "BBPS",
          score_value: "030",
          score_type: "TXNRISK",
        },
      ],
    };

    if (reqBody.additional_validation_details) {
      finalBody.additional_validation_details = reqBody.additional_validation_details;
    }

    if (reqBody.payment_amount) {
      finalBody.payment_amount = reqBody.payment_amount;
    }

    return { flow: "validate", body: finalBody };
  } catch (err) {
    return {
      flow: "error",
      error: {
        code: 5000,
        message: `Unexpected error while building body: ${err.message}`,
      },
    };
  }
};

module.exports = { buildValidateBody };
