const { GraphQLClient } = require("graphql-request");
const queries = require("../graphql-client/queries");
const CreateRRLogEvent = require("./rr-logger-event");
const { extractRRXmlPair } = require("./rr-log-xml");

/** Get bearer token from the socket (same approach used elsewhere) */
const getAuthToken = (socket) =>
  (socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token) || null;

/** Compact metadata for RR */
const buildRRExportMeta = ({ result, extra = {} }) => {
  const tx = result?.statusBlocks?.transaction;
  const rawRoStatus = result?.roStatus || result?.data?.roStatus || null;

  const roStatus =
    rawRoStatus ||
    (tx
      ? {
          status: tx.status ?? tx.Status,
          statusCode: tx.statusCode ?? tx.StatusCode,
          message: tx.message ?? tx.Message
        }
      : null);

  return {
    provider: "rr",
    success: Boolean(
      result?.success || (roStatus && String(roStatus.status || roStatus.Status).toUpperCase() === "SUCCESS")
    ),
    customerNo: result?.customerNo,
    svId: result?.svId,
    roStatus: roStatus && {
      status: roStatus.status ?? roStatus.Status,
      statusCode: roStatus.statusCode ?? roStatus.StatusCode,
      message: roStatus.message ?? roStatus.Message
    },
    statusBlocks: result?.statusBlocks || undefined,
    xml: result?.xml,
    parsed: result?.parsed,
    ...extra
  };
};

/** Build a stringified JSON array for the `message` text column */
const buildMessageJSONString = ({ error, classification, result, fallback }) => {
  const msgs = [];

  const clean = (v) => {
    if (v == null) return null;
    try {
      const s = String(v).replace(/\s+/g, " ").trim();
      return s.length ? s : null;
    } catch {
      return null;
    }
  };

  const push = (v) => {
    const s = clean(v);
    if (s && !msgs.includes(s)) msgs.push(s);
  };

  // Friendly first
  push(classification?.friendlyMessage);
  push(classification?.title);

  // Error text
  if (error instanceof Error) push(error.message);
  else if (typeof error === "string") push(error);
  else if (error?.message) push(error.message);

  // RR status message
  push(
    result?.roStatus?.message ??
      result?.roStatus?.Message ??
      result?.statusBlocks?.transaction?.message ??
      result?.statusBlocks?.transaction?.Message
  );

  // Fallback
  push(fallback || "RR export failed");

  const arr = msgs.length ? msgs : ["RR export failed"];
  return JSON.stringify(arr);
};

/**
 * Success: mark job exported + (optionally) insert a success log.
 * Uses queries.MARK_JOB_EXPORTED (same shape as Fortellis/PBS).
 * @param {boolean} isEarlyRo - If true, only logs success but does NOT change job status (for early RO creation)
 */
const markRRExportSuccess = async ({ socket, jobId, job, bodyshop, result, metaExtra = {}, isEarlyRo = false }) => {
  const endpoint = process.env.GRAPHQL_ENDPOINT;
  if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");
  const token = getAuthToken(socket);
  if (!token) throw new Error("Auth token missing on socket");

  const client = new GraphQLClient(endpoint, {});
  client.setHeaders({ Authorization: `Bearer ${token}` });

  const meta = buildRRExportMeta({ result, extra: metaExtra });

  // For early RO, we only insert a log but do NOT change job status or mark as exported
  if (isEarlyRo) {
    try {
      await client.request(queries.INSERT_EXPORT_LOG, {
        logs: [
          {
            bodyshopid: bodyshop?.id || job?.bodyshop?.id,
            jobid: jobId,
            successful: true,
            useremail: socket?.user?.email || null,
            metadata: meta,
            message: buildMessageJSONString({ result, fallback: "RR early RO created" })
          }
        ]
      });

      CreateRRLogEvent(socket, "INFO", "RR early RO: success log inserted (job status unchanged)", {
        jobId
      });
    } catch (e) {
      CreateRRLogEvent(socket, "ERROR", "RR early RO: failed to insert success log", {
        jobId,
        error: e?.message
      });
    }
    return;
  }

  // Full export: mark job as exported and insert success log
  const exportedStatus =
    job?.bodyshop?.md_ro_statuses?.default_exported || bodyshop?.md_ro_statuses?.default_exported || "Exported*";

  try {
    await client.request(queries.MARK_JOB_EXPORTED, {
      jobId,
      job: {
        status: exportedStatus,
        date_exported: new Date()
      },
      log: {
        bodyshopid: bodyshop?.id || job?.bodyshop?.id,
        jobid: jobId,
        successful: true,
        useremail: socket?.user?.email || null,
        metadata: meta,
        message: buildMessageJSONString({ result, fallback: "RR export succeeded" })
      },
      bill: {
        exported: true,
        exported_at: new Date()
      }
    });

    CreateRRLogEvent(socket, "INFO", "RR export: job marked exported + success log inserted", {
      jobId,
      exportedStatus
    });
  } catch (e) {
    CreateRRLogEvent(socket, "ERROR", "RR export: failed to persist success markers/log", {
      jobId,
      error: e?.message
    });
  }
};

/**
 * Failure: insert failure ExportsLog with `message` as JSON **string** (text column).
 * Uses queries.INSERT_EXPORT_LOG($logs: [exportlog_insert_input!]!).
 */
const insertRRFailedExportLog = async ({ socket, jobId, job, bodyshop, error, classification, result }) => {
  const endpoint = process.env.GRAPHQL_ENDPOINT;
  if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");

  const token = getAuthToken(socket);
  if (!token) throw new Error("Auth token missing on socket");

  const client = new GraphQLClient(endpoint, {});
  client.setHeaders({ Authorization: `Bearer ${token}` });

  const { requestXml, responseXml } = extractRRXmlPair(error);
  const xmlFromError =
    requestXml || responseXml
      ? {
          ...(requestXml ? { request: requestXml } : {}),
          ...(responseXml ? { response: responseXml } : {})
        }
      : undefined;

  const meta = buildRRExportMeta({
    result,
    extra: {
      error: error?.message || String(error),
      classification: classification || undefined,
      ...(requestXml ? { requestXml } : {}),
      ...(responseXml ? { responseXml } : {}),
      ...(xmlFromError && !result?.xml ? { xml: xmlFromError } : {})
    }
  });

  const message = buildMessageJSONString({
    error,
    classification,
    result,
    fallback: "RR export failed"
  });

  const entry = {
    bodyshopid: bodyshop?.id || job?.bodyshop?.id,
    jobid: jobId,
    successful: false,
    message, // stringified JSON array
    useremail: socket?.user?.email || null,
    metadata: meta
  };

  try {
    // Your mutation expects $logs (array). Keep to that signature.
    await client.request(queries.INSERT_EXPORT_LOG, { logs: [entry] });
    CreateRRLogEvent(socket, "INFO", "RR export: failure log inserted", { jobId });
  } catch (e) {
    CreateRRLogEvent(socket, "ERROR", "RR export: failed to insert failure log", {
      jobId,
      error: e?.message
    });
  }
};

module.exports = {
  markRRExportSuccess,
  insertRRFailedExportLog
};
