const admin = require("firebase-admin");
const client = require("../../../graphql-client/graphql-client").client;
const DefaultNewShop = require("../defaultNewShop.json");

const {
  CHECK_EXTERNAL_SHOP_ID,
  CREATE_SHOP,
  DELETE_VENDORS_BY_SHOP,
  DELETE_SHOP,
  CREATE_USER,
  UPDATE_BODYSHOP_BY_ID
} = require("../partsManagement.queries");

/**
 * Checks if the required fields are present in the payload.
 * @param payload
 * @param fields
 */
const requireFields = (payload, fields) => {
  for (const field of fields) {
    if (!payload[field]) {
      throw { status: 400, message: `${field} is required.` };
    }
  }
};

/**
 * Ensures that the provided email is not already registered in Firebase.
 * @param email
 * @returns {Promise<void>}
 */
const ensureEmailNotRegistered = async (email) => {
  try {
    await admin.auth().getUserByEmail(email);
    throw { status: 400, message: "userEmail is already registered in Firebase." };
  } catch (err) {
    if (err.code !== "auth/user-not-found") {
      throw { status: 500, message: "Error validating userEmail uniqueness", detail: err };
    }
  }
};

/**
 * Creates a new Firebase user with the given email and optional password.
 * @param email
 * @param password
 * @returns {Promise<UserRecord>}
 */
const createFirebaseUser = async (email, password = null) => {
  const userData = { email };
  if (password) userData.password = password;
  return admin.auth().createUser(userData);
};

/**
 * Deletes a Firebase user by UID.
 * @param uid
 * @returns {Promise<void>}
 */
const deleteFirebaseUser = async (uid) => {
  return admin.auth().deleteUser(uid);
};

/**
 * Generates a password reset link for the given email.
 * @param email
 * @returns {Promise<string>}
 */
const generateResetLink = async (email) => {
  return admin.auth().generatePasswordResetLink(email);
};

/**
 * Ensures that the provided external shop ID is unique.
 * @param externalId
 * @returns {Promise<void>}
 */
const ensureExternalIdUnique = async (externalId) => {
  const resp = await client.request(CHECK_EXTERNAL_SHOP_ID, { key: externalId });

  return !!resp.bodyshops.length;
};

/**
 * Inserts a new bodyshop into the database.
 * @param input
 * @returns {Promise<*>}
 */
const insertBodyshop = async (input) => {
  const resp = await client.request(CREATE_SHOP, { bs: input });
  return resp.insert_bodyshops_one.id;
};

/**
 * Deletes all vendors associated with a shop.
 * @param shopId
 * @returns {Promise<void>}
 */
const deleteVendorsByShop = async (shopId) => {
  await client.request(DELETE_VENDORS_BY_SHOP, { shopId });
};

/**
 * Deletes a bodyshop from the database.
 * @param shopId
 * @returns {Promise<void>}
 */
const deleteBodyshop = async (shopId) => {
  await client.request(DELETE_SHOP, { id: shopId });
};

/**
 * Inserts a new user association into the database.
 * @param uid
 * @param email
 * @param shopId
 * @returns {Promise<*>}
 */
const insertUserAssociation = async (uid, email, shopId) => {
  const vars = {
    u: {
      email,
      authid: uid,
      validemail: true,
      associations: {
        data: [{ shopid: shopId, authlevel: 99, active: true }]
      }
    }
  };
  const resp = await client.request(CREATE_USER, vars);
  return resp.insert_users_one;
};

/**
 * PATCH handler for updating bodyshop fields.
 * Allows patching: shopname, address1, address2, city, state, zip_post, country, email, timezone, phone
 * Also allows updating logo_img_path via a simple logoUrl string, which is expanded to the full object.
 * @param req
 * @param res
 * @returns {Promise<void>}
 */
const patchPartsManagementProvisioning = async (req, res) => {
  const { id } = req.params;

  // Fields that can be directly patched 1:1
  const allowedFields = [
    "shopname",
    "address1",
    "address2",
    "city",
    "state",
    "zip_post",
    "country",
    "email",
    "timezone",
    "phone"
    // NOTE: logo_img_path is handled separately via logoUrl
  ];

  const updateFields = {};

  // Copy over simple scalar fields if present
  for (const field of allowedFields) {
    if (req.body[field] !== undefined) {
      updateFields[field] = req.body[field];
    }
  }

  // Handle logo update via a simple href string, same behavior as provision route
  if (typeof req.body.logo_img_path === "string") {
    const trimmed = req.body.logo_img_path.trim();
    if (trimmed) {
      updateFields.logo_img_path = {
        src: trimmed,
        width: "",
        height: "",
        headerMargin: DefaultNewShop.logo_img_path.headerMargin
      };
    }
  }

  if (Object.keys(updateFields).length === 0) {
    return res.status(400).json({ error: "No valid fields provided for update." });
  }

  // Check that the bodyshop has an external_shop_id before allowing patch
  try {
    const shopResp = await client.request(
      `query GetBodyshop($id: uuid!) {
        bodyshops_by_pk(id: $id) {
          id
          external_shop_id
        }
      }`,
      { id }
    );

    if (!shopResp.bodyshops_by_pk?.external_shop_id) {
      return res.status(400).json({ error: "Cannot patch: bodyshop does not have an external_shop_id." });
    }
  } catch (err) {
    return res.status(500).json({
      error: "Failed to validate bodyshop external_shop_id.",
      detail: err
    });
  }

  try {
    const resp = await client.request(UPDATE_BODYSHOP_BY_ID, { id, fields: updateFields });
    if (!resp.update_bodyshops_by_pk) {
      return res.status(404).json({ error: "Bodyshop not found." });
    }
    return res.json(resp.update_bodyshops_by_pk);
  } catch (err) {
    return res.status(500).json({ error: "Failed to update bodyshop.", detail: err });
  }
};

/**
 * Handles provisioning a new shop for parts management.
 * @param req
 * @param res
 * @returns {Promise<*>}
 */
const partsManagementProvisioning = async (req, res) => {
  const { logger } = req;

  // Trim and normalize email early
  const body = {
    ...req.body,
    userEmail: req.body.userEmail?.trim().toLowerCase()
  };

  const trim = (value) => (typeof value === "string" ? value.trim() : value);
  const trimIfString = (value) =>
    value !== null && value !== undefined && typeof value === "string" ? value.trim() : value;

  try {
    // Ensure email is present and trimmed before checking registration
    if (!body.userEmail) {
      throw { status: 400, message: "userEmail is required" };
    }

    await ensureEmailNotRegistered(body.userEmail);

    requireFields(body, [
      "external_shop_id",
      "shopname",
      "address1",
      "city",
      "state",
      "zip_post",
      "country",
      "email",
      "phone",
      "userEmail"
    ]);

    // Trim all top-level string fields
    const trimmedBody = {
      ...body,
      external_shop_id: trim(body.external_shop_id),
      shopname: trim(body.shopname),
      address1: trim(body.address1),
      address2: trimIfString(body.address2),
      city: trim(body.city),
      state: trim(body.state),
      zip_post: trim(body.zip_post),
      country: trim(body.country),
      email: trim(body.email),
      phone: trim(body.phone),
      timezone: trimIfString(body.timezone),
      logoUrl: trimIfString(body.logoUrl),
      userPassword: body.userPassword, // passwords should NOT be trimmed (preserves intentional spaces if any, though rare)
      vendors: Array.isArray(body.vendors)
        ? body.vendors.map((v) => ({
            name: trim(v.name),
            street1: trimIfString(v.street1),
            street2: trimIfString(v.street2),
            city: trimIfString(v.city),
            state: trimIfString(v.state),
            zip: trimIfString(v.zip),
            country: trimIfString(v.country),
            email: trimIfString(v.email),
            cost_center: trimIfString(v.cost_center),
            phone: trimIfString(v.phone),
            dmsid: trimIfString(v.dmsid),
            discount: v.discount ?? 0,
            due_date: v.due_date ?? null,
            favorite: v.favorite ?? [],
            active: v.active ?? true
          }))
        : []
    };

    const duplicateCheck = await ensureExternalIdUnique(trimmedBody.external_shop_id);

    if (duplicateCheck) {
      throw { status: 400, message: `external_shop_id '${trimmedBody.external_shop_id}' is already in use.` };
    }

    logger.log("admin-create-shop-user", "debug", trimmedBody.userEmail, null, {
      request: req.body,
      ioadmin: true
    });

    const shopInput = {
      shopname: trimmedBody.shopname,
      address1: trimmedBody.address1,
      address2: trimmedBody.address2,
      city: trimmedBody.city,
      state: trimmedBody.state,
      zip_post: trimmedBody.zip_post,
      country: trimmedBody.country,
      email: trimmedBody.email,
      external_shop_id: trimmedBody.external_shop_id,
      timezone: trimmedBody.timezone || DefaultNewShop.timezone,
      phone: trimmedBody.phone,
      logo_img_path: {
        src: trimmedBody.logoUrl || null, // allow empty logo
        width: "",
        height: "",
        headerMargin: DefaultNewShop.logo_img_path.headerMargin
      },
      features: {
        allAccess: false,
        partsManagementOnly: true
      },
      md_ro_statuses: DefaultNewShop.md_ro_statuses,
      md_order_statuses: DefaultNewShop.md_order_statuses,
      md_responsibility_centers: DefaultNewShop.md_responsibility_centers,
      md_referral_sources: DefaultNewShop.md_referral_sources,
      md_messaging_presets: DefaultNewShop.md_messaging_presets,
      md_rbac: DefaultNewShop.md_rbac,
      md_classes: DefaultNewShop.md_classes,
      md_ins_cos: DefaultNewShop.md_ins_cos,
      md_categories: DefaultNewShop.md_categories,
      md_labor_rates: DefaultNewShop.md_labor_rates,
      md_payment_types: DefaultNewShop.md_payment_types,
      md_hour_split: DefaultNewShop.md_hour_split,
      md_ccc_rates: DefaultNewShop.md_ccc_rates,
      appt_alt_transport: DefaultNewShop.appt_alt_transport,
      md_jobline_presets: DefaultNewShop.md_jobline_presets,
      vendors: {
        data: trimmedBody.vendors.map((v) => ({
          name: v.name,
          street1: v.street1,
          street2: v.street2,
          city: v.city,
          state: v.state,
          zip: v.zip,
          country: v.country,
          email: v.email,
          discount: v.discount,
          due_date: v.due_date,
          cost_center: v.cost_center,
          favorite: v.favorite,
          phone: v.phone,
          active: v.active,
          dmsid: v.dmsid
        }))
      }
    };

    const newShopId = await insertBodyshop(shopInput);
    const userRecord = await createFirebaseUser(trimmedBody.userEmail, trimmedBody.userPassword);
    let resetLink = null;
    if (!trimmedBody.userPassword) {
      resetLink = await generateResetLink(trimmedBody.userEmail);
    }

    const createdUser = await insertUserAssociation(userRecord.uid, trimmedBody.userEmail, newShopId);

    return res.status(200).json({
      shop: { id: newShopId, shopname: trimmedBody.shopname },
      user: {
        id: createdUser.id,
        email: createdUser.email,
        resetLink: resetLink || undefined
      }
    });
  } catch (err) {
    logger.log("admin-create-shop-user-error", "error", body.userEmail || "unknown", null, {
      message: err.message,
      detail: err.detail || err
    });

    if (err.userRecord) {
      await deleteFirebaseUser(err.userRecord.uid).catch(() => {
        /* empty */
      });
    }
    if (err.newShopId) {
      await deleteVendorsByShop(err.newShopId).catch(() => {
        /* empty */
      });
      await deleteBodyshop(err.newShopId).catch(() => {
        /* empty */
      });
    }

    return res.status(err.status || 500).json({ error: err.message || "Internal server error" });
  }
};

module.exports = { partsManagementProvisioning, patchPartsManagementProvisioning };
