const dbSAPPS = require("../../helpers/db");
require("dotenv").config();
const { Op } = require("sequelize");

module.exports = {
  create,
  getAll,
  getVendor,
  option,
  count,
  update,
  enabled,
  delete: _delete,
};

const formatWithModifier = async (bankAccounts) => {
  return Promise.all(
    bankAccounts.map(async (bankAccount) => {
      const modifier = await dbSAPPS.User.findOne({
        where: {
          id: bankAccount.modified_by,
        },
        attributes: ["full_name"],
      });

      return {
        ...bankAccount,
        modifier: modifier?.full_name,
      };
    }),
  );
};

async function getAll(params) {
  try {
    // filter
    const filterConditions = {};

    if (params.companiesId) {
      filterConditions.company_id = {
        [Op.in]: params.companiesId,
      };
    }

    if (params.employeesId) {
      filterConditions.employee_id = {
        [Op.in]: params.employeesId,
      };
    }

    let bankAccounts = [];
    bankAccounts = await dbSAPPS.BankAccount.findAll({
      where: filterConditions,
      attributes: [
        "id",
        "display_name",
        "account_number",
        "company_id",
        "employee_id",
        "bank_id",
        "client_id",
        "type",
        "branch",
        "name",
        "description",
        "enabled",
        "modified_by",
        "modified",
        "is_verified",
      ],
      order: [["modified", "DESC"]],
      include: [
        {
          model: dbSAPPS.Company,
          as: "company",
          attributes: ["id", "display_name"],
        },
        {
          model: dbSAPPS.Employee,
          as: "employee",
          attributes: ["id", "employee_name", "employee_number"],
        },
        {
          model: dbSAPPS.Bank,
          as: "bank",
          attributes: ["id", "display_name", "code"],
        },
      ],
    });

    bankAccounts = bankAccounts.map((bankAccount) => {
      return {
        ...bankAccount.dataValues,
        company_name: bankAccount?.company?.display_name,
        employee_name: bankAccount?.employee?.employee_name,
        bank_name: bankAccount?.bank?.display_name,
        bank_code: bankAccount?.bank?.code,
      };
    });

    bankAccounts = await formatWithModifier(bankAccounts);

    return {
      bankAccounts,
      success: true,
      message: "Loaded successfully!",
    };
  } catch (error) {
    const errorMessage = error?.message || "Failed to load, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function getVendor() {
  try {
    // filter
    let vendors = [];
    vendors = await dbSAPPS.BankAccount.findAll({
      where: {
        type: "Vendor",
        enabled: true,
      },
      attributes: [
        "id",
        "display_name",
        "account_number",
        "company_id",
        "employee_id",
        "bank_id",
        "client_id",
        "type",
        "branch",
        "name",
        "description",
        "enabled",
      ],
      order: [["display_name", "ASC"]],
      include: [
        {
          model: dbSAPPS.Company,
          as: "company",
          attributes: ["id", "display_name"],
        },
        {
          model: dbSAPPS.Employee,
          as: "employee",
          attributes: ["id", "employee_name", "employee_number"],
        },
        {
          model: dbSAPPS.Client,
          as: "client",
          attributes: ["id", "display_name"],
        },
        {
          model: dbSAPPS.Bank,
          as: "bank",
          attributes: ["id", "display_name", "code"],
        },
      ],
    });

    vendors = vendors.map((option) => {
      return {
        ...option.dataValues,
        company_name: option?.company?.display_name,
        employee_name: option?.employee?.employee_name,
        bank_name: option?.bank?.display_name,
        bank_code: option?.bank?.code,
      };
    });

    return {
      vendors,
      success: true,
      message: "Loaded successfully!",
    };
  } catch (error) {
    const errorMessage = error?.message || "Failed to load, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function option(params) {
  try {
    // filter
    const filterConditions = {};

    if (params.companiesId) {
      filterConditions.company_id = {
        [Op.in]: params.companiesId,
      };
    }

    if (params.employeesId) {
      filterConditions.employee_id = {
        [Op.in]: params.employeesId,
      };
    }

    let options = [];
    options = await dbSAPPS.BankAccount.findAll({
      where: {
        ...filterConditions,
        enabled: true,
      },
      attributes: [
        "id",
        "display_name",
        "account_number",
        "company_id",
        "employee_id",
        "bank_id",
        "client_id",
        "type",
        "branch",
        "name",
        "description",
        "enabled",
      ],
      order: [["display_name", "ASC"]],
      include: [
        {
          model: dbSAPPS.Company,
          as: "company",
          attributes: ["id", "display_name"],
        },
        {
          model: dbSAPPS.Employee,
          as: "employee",
          attributes: ["id", "employee_name", "employee_number"],
        },
        {
          model: dbSAPPS.Client,
          as: "client",
          attributes: ["id", "display_name"],
        },
        {
          model: dbSAPPS.Bank,
          as: "bank",
          attributes: ["id", "display_name", "code"],
        },
      ],
    });

    options = options.map((option) => {
      return {
        ...option.dataValues,
        company_name: option?.company?.display_name,
        employee_name: option?.employee?.employee_name,
        bank_name: option?.bank?.display_name,
        bank_code: option?.bank?.code,
      };
    });

    return {
      options,
      success: true,
      message: "Loaded successfully!",
    };
  } catch (error) {
    const errorMessage = error?.message || "Failed to load, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function count(params) {
  try {
    // filter
    const filterConditions = {};

    const count = await dbSAPPS.BankAccount.count({
      where: filterConditions,
    });

    return {
      count,
      success: true,
      message: "Loaded successfully!",
    };
  } catch (error) {
    const errorMessage = error?.message || "Failed to load, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function create(params) {
  const transaction = await dbSAPPS.sequelize.transaction();
  try {
    // validate existing
    const existingDisplayName = await dbSAPPS.BankAccount.findOne({
      where: {
        name: params.name,
        display_name: params.display_name,
        company_id: params.company_id,
        bank_id: params.bank_id,
        [Op.or]: [{ employee_id: params.employee_id }, { client_id: params.client_id }],
      },
      transaction,
    });
    if (existingDisplayName) {
      return {
        success: false,
        message: `"${params.display_name}" is already taken.`,
      };
    }

    // save
    await dbSAPPS.BankAccount.create(params, { transaction });

    await transaction.commit();
    return {
      success: true,
      message: "Saved successfully!",
    };
  } catch (error) {
    await transaction.rollback();
    const errorMessage = error?.message || "Failed to save, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function update(id, params) {
  const transaction = await dbSAPPS.sequelize.transaction();
  try {
    // validate existing
    const data = await dbSAPPS.BankAccount.findByPk(id);
    if (!data) return { success: false, message: "ID not found!" };

    // validate existing
    const existingDisplayName = await dbSAPPS.BankAccount.findOne({
      where: {
        name: params.name,
        display_name: params.display_name,
        company_id: params.company_id,
        bank_id: params.bank_id,
        employee_id: params.employee_id,
        id: {
          [Op.ne]: id,
        },
      },
      transaction,
    });
    if (existingDisplayName) {
      return {
        success: false,
        message: `"${params.display_name}" is already taken.`,
      };
    }

    // save
    await data.update(params, { transaction });

    await transaction.commit();
    return {
      success: true,
      message: "Saved successfully!",
    };
  } catch (error) {
    await transaction.rollback();
    const errorMessage = error?.message || "Failed to save, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function enabled(id, params) {
  const transaction = await dbSAPPS.sequelize.transaction();
  try {
    // validate existing
    const data = await dbSAPPS.BankAccount.findByPk(id);
    if (!data) return { success: false, message: "ID not found!" };

    // update params
    const updateData = {};
    updateData.enabled = params.enabled;
    if (params.modified_by) updateData.modified_by = params.modified_by;
    if (params.modified) updateData.modified = params.modified;

    // save
    await data.update(updateData, { transaction });

    await transaction.commit();
    return {
      success: true,
      message: "Saved successfully!",
    };
  } catch (error) {
    await transaction.rollback();
    const errorMessage = error?.message || "Failed to save, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}

async function _delete(id) {
  const transaction = await dbSAPPS.sequelize.transaction();
  try {
    // validate existing
    const data = await dbSAPPS.BankAccount.findByPk(id, { transaction });
    if (!data) return { success: false, message: "ID not found!" };

    // delete
    await data.destroy({ transaction });

    await transaction.commit();
    return {
      success: true,
      message: "Deleted successfully!",
    };
  } catch (error) {
    await transaction.rollback();
    const errorMessage = error?.message || "Failed to delete, please try again!";
    return {
      success: false,
      message: errorMessage,
    };
  }
}
