package com.utopiaindustries.dao.uind;

import com.utopiaindustries.model.uind.PurchaseOrder;
import com.utopiaindustries.util.KeyHolderFunctions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class PurchaseOrderDAO {

    private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private final String TABLE_NAME = "inventory.purchase_order";
    private final String SELECT_QUERY = String.format("SELECT * FROM %s WHERE id = :id", TABLE_NAME);
    private final String SELECT_BY_IDS = String.format("SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME);
    private final String SELECT_BY_SUPPLIER_QUERY = String.format("SELECT * FROM %s WHERE supplier_id = :supplier_id", TABLE_NAME);
    private final String SELECT_WITH_MAIN_PAYMENT_TERM_IDS_AND_NOT_IN_ID_QUERY = String.format("SELECT * FROM %s WHERE id NOT IN (:ids) AND main_payment_term_id IN (:payment_term_ids) and management_status = :management_status AND user_status != :user_status", TABLE_NAME);
    private final String SELECT_LATEST_PO_NON_DRAFTED = String.format("SELECT * FROM %s WHERE id IN (:ids) AND user_status != :user_status ORDER BY id DESC LIMIT 1", TABLE_NAME);
    private final String SELECT_WITH_MAIN_PAYMENT_TERM_IDS_AND_NOT_IN_ID_QUERY_ADVANCE_PAYMENT = String.format("SELECT * FROM %s WHERE id NOT IN (:ids) AND main_payment_term_id IN (:payment_term_ids) and management_status = :management_status AND user_status != :user_status AND DATE(date_generated) >= :st_date AND DATE(date_generated) <= :ed_date", TABLE_NAME);
    private final String SELECT_BY_SUPPLIER_AND_USER_STATUS_QUERY = String.format("SELECT * FROM %s WHERE supplier_id = :supplier_id AND user_status = :user_status", TABLE_NAME);
    private final String SELECT_BY_IDS_AND_MANAGEMENT_STATUS = String.format("SELECT * FROM %s WHERE id IN (:ids) AND management_status = :management_status", TABLE_NAME);
    private final String SELECT_WITH_UNBILLED_CHARGES_BY_IDS = String.format("SELECT * FROM %s WHERE id IN (:ids) AND are_charges_paid = false", TABLE_NAME);
    private final String SELECT_BY_DEPARTMENT_QUERY = String.format("SELECT * FROM %s WHERE department_id = :department_id", TABLE_NAME);
    private final String SELECT_BY_FUNCTION_QUERY = String.format("SELECT * FROM %s WHERE function_id = :function_id", TABLE_NAME);
    private final String SELECT_LIKE_CODE_QUERY = String.format("SELECT * FROM %s WHERE code LIKE :code", TABLE_NAME);
    private final String SELECT_LIKE_CODE_BY_MANAGEMENT_STATUS_QUERY = String.format("SELECT * FROM %s WHERE code LIKE :code AND management_status = :management_status", TABLE_NAME);
    private final String SELECT_BY_USER_STATUS_QUERY = String.format("SELECT * FROM %s WHERE user_status = :user_status", TABLE_NAME);
    private final String SELECT_BY_USER_STATUS_AND_COMPLETED_QUERY = String.format("SELECT * FROM %s WHERE user_status = :user_status AND completed = :completed", TABLE_NAME);
    private final String SELECT_BY_MANAGEMENT_STATUS_QUERY = String.format("SELECT * FROM %s WHERE management_status = :management_status", TABLE_NAME);
    private final String SELECT_BY_MANAGEMENT_STATUS_QUERY_TOP_N = String.format("SELECT * FROM %s WHERE management_status = :management_status ORDER BY id DESC LIMIT :no_rows", TABLE_NAME);
    private final String SELECT_BY_INDENT_DIV_APPROVAL_QUERY_TOP_N = String.format("SELECT * FROM %s WHERE indenter_div_head_status = :indenter_div_head_status ORDER BY id DESC LIMIT :no_rows", TABLE_NAME);
    private final String SELECT_BY_MANAGEMENT_STATUS_AND_DATE_QUERY = String.format("SELECT * FROM %s WHERE management_status = :management_status AND date_generated >= :date_generated", TABLE_NAME);
    private final String SELECT_APPROVAL_REQUESTS_QUERY = String.format("SELECT * FROM %s WHERE management_status = 'REVIEWING' OR management_status = 'ONHOLD'", TABLE_NAME);
    private final String SELECT_APPROVAL_REQUESTS_QUERY_TOP_N = String.format("SELECT * FROM %s WHERE management_status = 'REVIEWING' OR management_status = 'ONHOLD' LIMIT :no_rows", TABLE_NAME);
    private final String SELECT_INDENT_PO_APPROVAL_REQUESTS_QUERY_TOP_N = String.format("SELECT * FROM %s WHERE indenter_div_head_status = 'REVIEWING' OR indenter_div_head_status = 'ONHOLD' LIMIT :no_rows", TABLE_NAME);
    private final String SELECT_COMPLETED_BY_SUPPLIER_QUERY = String.format("SELECT * FROM %s WHERE supplier_id = :supplier_id AND completed = 1", TABLE_NAME);
    private final String SELECT_BY_START_DATE_USER_STATUS_IN_PAYMENT_TERM_STATUS_QUERY = String.format("SELECT * FROM %s WHERE physical_date >= :start_date AND user_status = :user_status AND main_payment_term_id IN (:main_payment_term_ids) ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_BY_USER_STATUS_IN_PAYMENT_TERM_STATUS_QUERY = String.format("SELECT * FROM %s WHERE user_status = :user_status AND main_payment_term_id IN (:main_payment_term_ids) ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_BY_PAYMENT_TERMS_STATUS_QUERY = String.format("SELECT * FROM %s WHERE main_payment_term_id IN (:main_payment_term_ids) ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_BY_PARENT_DOCUMENT_QUERY = String.format("SELECT * FROM %s WHERE parent_document_type = :parent_document_type AND parent_document_id = :parent_document_id", TABLE_NAME);
    private final String SELECT_BY_PARENT_DOCUMENTS_QUERY = String.format("SELECT * FROM %s WHERE parent_document_type = :parent_document_type AND parent_document_id IN (:parent_document_id)", TABLE_NAME);
    private final String SELECT_BY_APPROVAL_DATE_RANGE_QUERY = String.format("SELECT * FROM %s WHERE DATE(approval_date) >= :start_date AND DATE(approval_date) <= :end_date ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_ALL_QUERY = String.format("SELECT * FROM %s ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_ALL_QUERY_TOP_N = String.format("SELECT * FROM %s ORDER BY id DESC LIMIT :no_rows", TABLE_NAME);
    private final String SELECT_BY_INDENT_GENERAL_IDS = String.format("SELECT * FROM %s WHERE parent_document_id IN (:parent_document_id)", TABLE_NAME);
    private final String SELECT_BY_INDENT_GENERAL_IDS_AND_LIMIT = String.format("SELECT * FROM %s WHERE parent_document_id IN (:parent_document_id) ORDER BY id ASC", TABLE_NAME);
    private final String DELETE_QUERY = String.format("DELETE FROM %s WHERE id = :id", TABLE_NAME);
    private final String INSERT_QUERY = String.format(
            "INSERT INTO %s (id, code, date_generated, physical_date, approval_date, indenter_div_approval_date, generated_by_user, approved_by_user, indenter_div_approved_by, company_id, function_id, department_id, section_id, quotation_reference_number, quotation_date, supplier_id, broker_id, delivery_location_site_id, user_remarks, user_status, management_status, management_remarks, indenter_div_head_status, indenter_div_head_remarks, shipment_mode, currency_symbol, currency_conversion_rate, gst_amount, gst_amount_in_pkr, total_cost, total_cost_in_pkr, total_cost_in_words, total_cost_in_words_in_pkr, outstanding_advance_amount, amount_paid, main_payment_term_id, date_completed, termination_reason, parent_document_type, parent_document_code, parent_document_id, indent_id, completed, is_rated, is_locked, are_charges_paid, is_urgent, is_flagged) VALUES (:id, :code, :date_generated, :physical_date, :approval_date, :indenter_div_approval_date, :generated_by_user, :approved_by_user, :indenter_div_approved_by, :company_id, :function_id, :department_id, :section_id, :quotation_reference_number, :quotation_date, :supplier_id, :broker_id, :delivery_location_site_id, :user_remarks, :user_status, :management_status, :management_remarks, :indenter_div_head_status, :indenter_div_head_remarks, :shipment_mode, :currency_symbol, :currency_conversion_rate, :gst_amount, :gst_amount_in_pkr, :total_cost, :total_cost_in_pkr, :total_cost_in_words, :total_cost_in_words_in_pkr, :outstanding_advance_amount, :amount_paid, :main_payment_term_id, :date_completed, :termination_reason, :parent_document_type, :parent_document_code, :parent_document_id, :indent_id, :completed, :is_rated, :is_locked, :are_charges_paid, :is_urgent, :is_flagged) ON DUPLICATE KEY UPDATE code = :code, date_generated = :date_generated, physical_date = :physical_date, approval_date = :approval_date, indenter_div_approval_date = :indenter_div_approval_date, generated_by_user = :generated_by_user, approved_by_user = :approved_by_user, indenter_div_approved_by = :indenter_div_approved_by, company_id = :company_id, function_id = :function_id, department_id = :department_id, section_id = :section_id, quotation_reference_number = :quotation_reference_number, quotation_date = :quotation_date, supplier_id = :supplier_id, broker_id = :broker_id, delivery_location_site_id = :delivery_location_site_id, user_remarks = :user_remarks, user_status = :user_status, management_status = :management_status, management_remarks = :management_remarks, indenter_div_head_status = :indenter_div_head_status, indenter_div_head_remarks = :indenter_div_head_remarks, shipment_mode = :shipment_mode, currency_symbol = :currency_symbol, currency_conversion_rate = :currency_conversion_rate, gst_amount = :gst_amount, gst_amount_in_pkr = :gst_amount_in_pkr, total_cost = :total_cost, total_cost_in_pkr = :total_cost_in_pkr, total_cost_in_words = :total_cost_in_words, total_cost_in_words_in_pkr = :total_cost_in_words_in_pkr, outstanding_advance_amount = :outstanding_advance_amount, amount_paid = :amount_paid, main_payment_term_id = :main_payment_term_id, date_completed = :date_completed, termination_reason = :termination_reason, parent_document_type = :parent_document_type, parent_document_code = :parent_document_code, parent_document_id = :parent_document_id, indent_id = :indent_id, completed = :completed, is_rated = :is_rated, is_locked = :is_locked, are_charges_paid = :are_charges_paid, is_urgent = :is_urgent, is_flagged = :is_flagged",
            TABLE_NAME
    );
    private final String SELECT_PURCHASE_ORDER_BY_INDENT_GENERAL_NOT_EXITS = String.format("SELECT * FROM %s WHERE parent_document_id IS NULL AND management_status = :management_status AND user_status != :user_status", TABLE_NAME);
    // sum by supplier
    private final String SELECT_ITEM_SUBTOTAL_SUM_BY_SUPPLIER_QUERY = String.format("SELECT po.supplier_id, SUM((item.unit_price_in_pkr * item.quantity) + item.gst_in_pkr) AS total FROM %s item LEFT JOIN %s po ON item.purchase_order_id = po.id GROUP BY po.supplier_id", "inventory.purchase_order_item", TABLE_NAME);
    private final String SELECT_BY_PO_IDS_AND_ISCOMPLETED = String.format("SELECT * FROM %s WHERE id IN (:id) AND completed = :completed AND management_status = :management_status ORDER BY id DESC", TABLE_NAME);
    private final String SELECT_BY_GENERATED_BY_USER_AND_COMPANY_DETAILS_QUERY = String.format("SELECT * FROM %s WHERE generated_by_user = :generated_by_user AND company_id = :company_id AND function_id = :function_id AND department_id = :department_id AND section_id = :section_id", TABLE_NAME);

    private final String SELECT_BY_TERM = String.format( "SELECT * FROM %s WHERE code LIKE :term limit 100 offset 0", TABLE_NAME );

    public PurchaseOrderDAO(@Qualifier("namedParameterJdbcTemplateUind") NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    // prepare query params
    private MapSqlParameterSource prepareInsertQueryParams(PurchaseOrder purchaseOrder) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("id", purchaseOrder.getId())
                .addValue("code", purchaseOrder.getCode())
                .addValue("date_generated", purchaseOrder.getDateGenerated())
                .addValue("physical_date", purchaseOrder.getPhysicalDate())
                .addValue("approval_date", purchaseOrder.getApprovalDate())
                .addValue("indenter_div_approval_date", purchaseOrder.getIndentDivApprovalDate())
                .addValue("generated_by_user", purchaseOrder.getGeneratedByUser())
                .addValue("approved_by_user", purchaseOrder.getApprovedByUser())
                .addValue("indenter_div_approved_by", purchaseOrder.getIndenterDivApprovedByUser())
                .addValue("company_id", purchaseOrder.getCompanyId())
                .addValue("function_id", purchaseOrder.getFunctionId())
                .addValue("department_id", purchaseOrder.getDepartmentId())
                .addValue("section_id", purchaseOrder.getSectionId())
                .addValue("quotation_reference_number", purchaseOrder.getQuotationReferenceNumber())
                .addValue("quotation_date", purchaseOrder.getQuotationDate())
                .addValue("supplier_id", purchaseOrder.getSupplierId())
                .addValue("broker_id", purchaseOrder.getBrokerId())
                .addValue("delivery_location_site_id", purchaseOrder.getDeliveryLocationSiteId())
                .addValue("user_remarks", purchaseOrder.getUserRemarks())
                .addValue("user_status", purchaseOrder.getUserStatus())
                .addValue("management_status", purchaseOrder.getManagementStatus())
                .addValue("management_remarks", purchaseOrder.getManagementRemarks())
                .addValue("indenter_div_head_status", purchaseOrder.getIndenterDivPoStatus())
                .addValue("indenter_div_head_remarks", purchaseOrder.getIndenterDivPoRemarks())
                .addValue("shipment_mode", purchaseOrder.getShipmentMode())
                .addValue("currency_symbol", purchaseOrder.getCurrencySymbol())
                .addValue("currency_conversion_rate", purchaseOrder.getCurrencyConversionRate())
                .addValue("gst_amount", purchaseOrder.getGstAmount())
                .addValue("gst_amount_in_pkr", purchaseOrder.getGstAmountInPkr())
                .addValue("total_cost", purchaseOrder.getTotalCost())
                .addValue("total_cost_in_pkr", purchaseOrder.getTotalCostInPkr())
                .addValue("total_cost_in_words", purchaseOrder.getTotalCostInWords())
                .addValue("total_cost_in_words_in_pkr", purchaseOrder.getTotalCostInWordsInPkr())
                .addValue("outstanding_advance_amount", purchaseOrder.getOutstandingAdvanceAmount())
                .addValue("amount_paid", purchaseOrder.getAmountPaid())
                .addValue("main_payment_term_id", purchaseOrder.getMainPaymentTermId())
                .addValue("date_completed", purchaseOrder.getDateCompleted())
                .addValue("termination_reason", purchaseOrder.getTerminationReason())
                .addValue("parent_document_type", purchaseOrder.getParentDocumentType())
                .addValue("parent_document_code", purchaseOrder.getParentDocumentCode())
                .addValue("parent_document_id", purchaseOrder.getParentDocumentId())
                .addValue("indent_id", purchaseOrder.getIndentId())
                .addValue("is_flagged", purchaseOrder.getIsFlagged());
        return params;
    }

    // find
    public PurchaseOrder find(String id) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("id", id);
        List<PurchaseOrder> purchaseOrders = namedParameterJdbcTemplate.query(SELECT_QUERY, params, new PurchaseOrderRowMapper());
        if (purchaseOrders.size() > 0) {
            return purchaseOrders.get(0);
        } else {
            return new PurchaseOrder();
        }
    }

    // find by ids
    public List<PurchaseOrder> findByIds(List<Long> ids) {
        if (!ids.isEmpty()) {
            Map params = Collections.singletonMap("ids", ids);
            return namedParameterJdbcTemplate.query(SELECT_BY_IDS, params, new PurchaseOrderRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // select by supplier
    public List<PurchaseOrder> findBySupplierId(long supplierId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("supplier_id", supplierId);
        return namedParameterJdbcTemplate.query(SELECT_BY_SUPPLIER_QUERY, params, new PurchaseOrderRowMapper());
    }

    // select by supplier and user status
    public List<PurchaseOrder> findBySupplierId(long supplierId, String userStatus) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("supplier_id", supplierId);
        params.addValue("user_status", userStatus);
        return namedParameterJdbcTemplate.query(SELECT_BY_SUPPLIER_AND_USER_STATUS_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by ids
    public List<PurchaseOrder> findByIdsAndManagementStatus(List<Long> ids, String managementStatus) {
        if (!ids.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("ids", ids);
            params.addValue("management_status", managementStatus);
            return namedParameterJdbcTemplate.query(SELECT_BY_IDS_AND_MANAGEMENT_STATUS, params, new PurchaseOrderRowMapper());
        } else {
            return new ArrayList<>();
        }
    }


    // find all
    public List<PurchaseOrder> findAll() {
        return namedParameterJdbcTemplate.query(SELECT_ALL_QUERY, new PurchaseOrderRowMapper());
    }

    // find all by row limit
    public List<PurchaseOrder> findAllByRowLimit(long row_limit) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("no_rows", row_limit);
        return namedParameterJdbcTemplate.query(SELECT_ALL_QUERY_TOP_N, params, new PurchaseOrderRowMapper());
    }

    // find by department id
    public List<PurchaseOrder> findByDepartmentId(long departmentId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("department_id", departmentId);
        return namedParameterJdbcTemplate.query(SELECT_BY_DEPARTMENT_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by function id
    public List<PurchaseOrder> findByFunctionId(long functionId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("function_id", functionId);
        return namedParameterJdbcTemplate.query(SELECT_BY_FUNCTION_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by management status
    public List<PurchaseOrder> findByManagementStatus(String managementStatus) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("management_status", managementStatus);
        return namedParameterJdbcTemplate.query(SELECT_BY_MANAGEMENT_STATUS_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by management status limit by rows
    public List<PurchaseOrder> findByManagementStatusRowLimit(String managementStatus, long row_limit) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("management_status", managementStatus);
        params.addValue("no_rows", row_limit);
        return namedParameterJdbcTemplate.query(SELECT_BY_MANAGEMENT_STATUS_QUERY_TOP_N, params, new PurchaseOrderRowMapper());
    }

    // find by management status limit by rows
    public List<PurchaseOrder> findByIndentDivApprovalStatusRowLimit(String indenterDivStatus, long row_limit) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("indenter_div_head_status", indenterDivStatus);
        params.addValue("no_rows", row_limit);
        return namedParameterJdbcTemplate.query(SELECT_BY_INDENT_DIV_APPROVAL_QUERY_TOP_N, params, new PurchaseOrderRowMapper());
    }


    // find PO by management status 'APPROVED' and greater equals date generated
    public List<PurchaseOrder> findByManagementStatusAndDateGenerated(String managementStatus, String date) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("management_status", managementStatus);
        params.addValue("date_generated", date);
        return namedParameterJdbcTemplate.query(SELECT_BY_MANAGEMENT_STATUS_AND_DATE_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by user status
    public List<PurchaseOrder> findByUserStatus(String userStatus) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("user_status", userStatus);
        return namedParameterJdbcTemplate.query(SELECT_BY_USER_STATUS_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by user status and completed status
    public List<PurchaseOrder> findByUserStatusAndCompletedStatus(String userStatus, boolean completed) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("user_status", userStatus);
        params.addValue("completed", completed);
        return namedParameterJdbcTemplate.query(SELECT_BY_USER_STATUS_AND_COMPLETED_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by supplier id
    public List<PurchaseOrder> findCompletedBySupplierId(long supplierId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("supplier_id", supplierId);
        return namedParameterJdbcTemplate.query(SELECT_COMPLETED_BY_SUPPLIER_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find approval requests
    public List<PurchaseOrder> findApprovalRequests() {
        return namedParameterJdbcTemplate.query(SELECT_APPROVAL_REQUESTS_QUERY, new PurchaseOrderRowMapper());
    }

    // find approval requests by row limit
    public List<PurchaseOrder> findApprovalRequestsByRowLimit(long row_limit) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("no_rows", row_limit);
        return namedParameterJdbcTemplate.query(SELECT_APPROVAL_REQUESTS_QUERY_TOP_N, params, new PurchaseOrderRowMapper());
    }

    // find approval requests by row limit
    public List<PurchaseOrder> findIndentPOApprovalRequestsByRowLimit(long row_limit) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("no_rows", row_limit);
        return namedParameterJdbcTemplate.query(SELECT_INDENT_PO_APPROVAL_REQUESTS_QUERY_TOP_N, params, new PurchaseOrderRowMapper());
    }

    // find like code
    public List<PurchaseOrder> findLikeCode(String code) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("code", "%" + code + "%");
        return namedParameterJdbcTemplate.query(SELECT_LIKE_CODE_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find like code by management status
    public List<PurchaseOrder> findLikeCodeByManagementStatus(String code, String managementStatus) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("code", "%" + code + "%");
        params.addValue("management_status", managementStatus);
        return namedParameterJdbcTemplate.query(SELECT_LIKE_CODE_BY_MANAGEMENT_STATUS_QUERY, params, new PurchaseOrderRowMapper());
    }

    // find by query
    public List<PurchaseOrder> findByQuery(String customQuery) {
        return namedParameterJdbcTemplate.query(customQuery, new PurchaseOrderRowMapper());
    }

    // find by user status in payment term
    public List<PurchaseOrder> findByUserStatusInPaymentTerm(String userStatus, List<Long> paymentTermIds) {
        if (!paymentTermIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("user_status", userStatus);
            params.addValue("main_payment_term_ids", paymentTermIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_USER_STATUS_IN_PAYMENT_TERM_STATUS_QUERY, params, new PurchaseOrderRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find by payment term
    public List<PurchaseOrder> findByPaymentTerms(List<Long> paymentTermIds) {
        if (!paymentTermIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("main_payment_term_ids", paymentTermIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_PAYMENT_TERMS_STATUS_QUERY, params, new PurchaseOrderRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find by start date and user status in payment term
    public List<PurchaseOrder> findByStartDateAndUserStatusInPaymentTerm(String startDate, String userStatus, List<Long> paymentTermIds) {
        if (!paymentTermIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("start_date", startDate);
            params.addValue("user_status", userStatus);
            params.addValue("main_payment_term_ids", paymentTermIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_START_DATE_USER_STATUS_IN_PAYMENT_TERM_STATUS_QUERY, params, new PurchaseOrderRowMapper());
        } else {
            return new ArrayList<>();
        }
    }


    // find by parent documents
    public List<PurchaseOrder> findByParentDocument(String parentDocumentType, long parentDocumentId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("parent_document_type", parentDocumentType);
        params.addValue("parent_document_id", parentDocumentId);
        return namedParameterJdbcTemplate.query(SELECT_BY_PARENT_DOCUMENT_QUERY, params, new PurchaseOrderRowMapper());
    }

    public List<PurchaseOrder> findByParentDocuments(String parentDocumentType, List<Long> parentIds) {
        if (parentIds.isEmpty()) return new ArrayList<>();
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("parent_document_type", parentDocumentType);
        params.addValue("parent_document_id", parentIds);
        return namedParameterJdbcTemplate.query(SELECT_BY_PARENT_DOCUMENTS_QUERY, params, new PurchaseOrderRowMapper());

    }

    // find by approval date range and management status
    public List<PurchaseOrder> findByApprovalDateRange(String startDate, String endDate) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("start_date", startDate);
        params.addValue("end_date", endDate);
        return namedParameterJdbcTemplate.query(SELECT_BY_APPROVAL_DATE_RANGE_QUERY, params, new PurchaseOrderRowMapper());
    }

    // save
    public long save(PurchaseOrder purchaseOrder) {
        KeyHolder keyHolder = new GeneratedKeyHolder();
        MapSqlParameterSource params = prepareInsertQueryParams(purchaseOrder);
        namedParameterJdbcTemplate.update(INSERT_QUERY, params, keyHolder);
        return KeyHolderFunctions.getKey(purchaseOrder.getId(), keyHolder);
    }

    // save all
    public int[] saveAll(List<PurchaseOrder> purchaseOrders) {
        List<MapSqlParameterSource> batchArgs = new ArrayList<>();
        for (PurchaseOrder purchaseOrder : purchaseOrders) {
            MapSqlParameterSource params = prepareInsertQueryParams(purchaseOrder);
            batchArgs.add(params);
        }
        return namedParameterJdbcTemplate.batchUpdate(INSERT_QUERY, batchArgs.toArray(new MapSqlParameterSource[purchaseOrders.size()]));
    }

    // delete
    public boolean delete(long id) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("id", id);
        return namedParameterJdbcTemplate.update(DELETE_QUERY, params) > 0;
    }

    //get PO from indent general ids
    public List<PurchaseOrder> findByIndentGeneralIds(List<Long> indentGeneralIds) {
        if (!indentGeneralIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("parent_document_id", indentGeneralIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_INDENT_GENERAL_IDS, params, new PurchaseOrderRowMapper());
        }
        return new ArrayList<>();
    }

    //get PO from indent general ids
    public List<PurchaseOrder> findByIndentIds(List<Long> indentGeneralIds) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("parent_document_id", indentGeneralIds);
        return namedParameterJdbcTemplate.query(SELECT_BY_INDENT_GENERAL_IDS_AND_LIMIT, params, new PurchaseOrderRowMapper());
    }

    // find by generated by user and company details
    public List<PurchaseOrder> findByGeneratedByUserAndCompanyDetails(String username, long companyId, long functionId, long departmentId, long sectionId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("generated_by_user", username)
                .addValue("company_id", companyId)
                .addValue("function_id", functionId)
                .addValue("department_id", departmentId)
                .addValue("section_id", sectionId);
        return namedParameterJdbcTemplate.query(SELECT_BY_GENERATED_BY_USER_AND_COMPANY_DETAILS_QUERY, params, new PurchaseOrderRowMapper());
    }

    /*
    * find by term
    * */
    public List<PurchaseOrder> findByTerm( String term ){
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("term", "%" + term + "%" );
        return namedParameterJdbcTemplate.query( SELECT_BY_TERM, params, new PurchaseOrderRowMapper() );
    }
}