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
} = 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 });
  if (resp.bodyshops.length) {
    throw { status: 400, message: `external_shop_id '${externalId}' is already in use.` };
  }
};

/**
 * 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;
};

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

  try {
    await ensureEmailNotRegistered(body.userEmail);
    requireFields(body, [
      "external_shop_id",
      "shopname",
      "address1",
      "city",
      "state",
      "zip_post",
      "country",
      "email",
      "phone",
      "userEmail"
    ]);
    await ensureExternalIdUnique(body.external_shop_id);

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

    const shopInput = {
      shopname: body.shopname,
      address1: body.address1,
      address2: body.address2 || null,
      city: body.city,
      state: body.state,
      zip_post: body.zip_post,
      country: body.country,
      email: body.email,
      external_shop_id: body.external_shop_id,
      timezone: body.timezone || DefaultNewShop.timezone,
      phone: body.phone,
      logo_img_path: {
        src: body.logoUrl,
        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: body.vendors.map((v) => ({
          name: v.name,
          street1: v.street1 || null,
          street2: v.street2 || null,
          city: v.city || null,
          state: v.state || null,
          zip: v.zip || null,
          country: v.country || null,
          email: v.email || null,
          discount: v.discount ?? 0,
          due_date: v.due_date ?? null,
          cost_center: v.cost_center || null,
          favorite: v.favorite ?? [],
          phone: v.phone || null,
          active: v.active ?? true,
          dmsid: v.dmsid || null
        }))
      }
    };

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

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

    return res.status(200).json({
      shop: { id: newShopId, shopname: body.shopname },
      user: {
        id: createdUser.id,
        email: createdUser.email,
        resetLink: resetLink || undefined
      }
    });
  } catch (err) {
    logger.log("admin-create-shop-user-error", "error", body.userEmail, 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;
