package com.utopiaindustries.dao.uind;

import com.utopiaindustries.util.KeyHolderFunctions;
import org.springframework.beans.factory.annotation.Autowired;
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 com.utopiaindustries.model.uind.Item;

import java.util.*;


@Repository
public class ItemDAO {

    private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private final String TABLE_NAME = "inventory.item";
    private final String SELECT_QUERY = String.format("SELECT * FROM %s WHERE id = :id", TABLE_NAME);
    private final String SELECT_NOT_IN_IDS = String.format("SELECT * FROM %s WHERE id NOT IN (:ids)", TABLE_NAME);
    private final String SELECT_BY_TYPE_QUERY = String.format("SELECT * FROM %s WHERE type_id IN (:type_id)", TABLE_NAME);
    private final String SELECT_BY_SKU_TITLE_TYPE_QUERY_AND_IS_ACTIVE = String.format("SELECT * FROM %s WHERE (title LIKE :title OR sku LIKE :title) AND is_active = :is_active AND type_id IN (:type_ids)", TABLE_NAME);
    private final String SELECT_BY_CATEGORY_QUERY =
            String.format("SELECT * FROM %s WHERE category_id = :category_id OR subcategory_one_id = :category_id OR subcategory_two_id = :category_id", TABLE_NAME);
    private final String SELECT_BY_POSITION_CATEGORIES_QUERY =
            String.format("SELECT * FROM %s WHERE category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)", TABLE_NAME);
    private final String SELECT_BY_POSITION_CATEGORIES_QUERY_TOP_N_ROWS =
            String.format("SELECT * FROM %s WHERE category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids) ORDER BY id DESC LIMIT :no_rows ",
                    TABLE_NAME);
    private final String SELECT_BY_POSITION_CATEGORIES_AND_ITEM_REQUEST_ID_QUERY_TOP_N_ROWS = String.format(
            "SELECT * FROM %s WHERE (category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)) AND item_request_id != 0 ORDER BY id DESC LIMIT :no_rows ",
            TABLE_NAME);
    private final String SELECT_BY_POSITION_CATEGORIES_AND_ITEM_IDS_QUERY =
            String.format("SELECT * FROM %s WHERE category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids) AND id IN (:ids) ORDER BY id DESC",
                    TABLE_NAME);
    private final String SELECT_BY_CATEGORY_IDS = String.format("SELECT * FROM %s WHERE category_id IN (:category_ids)", TABLE_NAME);
    private final String SELECT_BY_CATEGORY_DEPARTMENT_QUERY = String.format(
            "SELECT * FROM %s WHERE department_id = :department_id AND (category_id = :category_id OR subcategory_one_id = :category_id OR subcategory_two_id = :category_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_IDS_AND_ACTIVE = String.format("SELECT * FROM %s WHERE id IN (:ids) AND is_active = 1 ", TABLE_NAME);
    private final String SELECT_LIKE_TITLE_AND_STATUS_QUERY = String.format("SELECT * FROM %s WHERE is_active = :is_active AND title LIKE :title ORDER BY title ASC limit 100 offset 0", TABLE_NAME);
    private final String SELECT_LIKE_TITLE_BY_DEPARTMENT_QUERY =
            String.format("SELECT * FROM %s WHERE department_id = :department_id AND title LIKE :title ORDER BY title ASC", TABLE_NAME);
    private final String SELECT_LIKE_TITLE = String.format("SELECT * FROM %s WHERE title LIKE :item_title", TABLE_NAME);
    private final String SELECT_LIKE_TITLE_BY_POSITION_CATEGORIES_QUERY = String.format(
            "SELECT * FROM %s WHERE title LIKE :title AND (category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)) ORDER BY title ASC",
            TABLE_NAME);
    private final String SELECT_LIKE_TITLE_SKU_BY_POSITION_CATEGORIES_AND_ACTIVE_QUERY = String.format(
            "SELECT * FROM %s WHERE is_active = :is_active AND (title LIKE :title OR sku LIKE :title) AND (category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)) ORDER BY title ASC",
            TABLE_NAME);
    private final String SELECT_LIKE_TITLE_SKU_BY_POSITION_CATEGORIES_AND_ACTIVE_AND_TYPE_IDS_QUERY = String.format(
            "SELECT * FROM %s WHERE is_active = :is_active AND (title LIKE :title OR sku LIKE :title) AND (category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)) AND type_id IN (:type_ids) ORDER BY title ASC LIMIT :limit",
            TABLE_NAME);
    private final String SELECT_EQUALS_SKU_QUERY = String.format("SELECT * FROM %s WHERE sku = :sku ORDER BY title ASC", TABLE_NAME);
    private final String SELECT_EQUALS_IN_SKU_QUERY = String.format("SELECT * FROM %s WHERE sku IN (:skus) ORDER BY title ASC", TABLE_NAME);
    private final String SELECT_ALL_QUERY = String.format("SELECT * FROM %s ORDER BY id DESC", 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, title, short_title, description, spec1, spec2, spec3, spec4, spec5, brand, year, model, unit_id, company_id, function_id, department_id, section_id, type_id, category_id, subcategory_one_id, subcategory_two_id, code, item_request_id, minimum_level, dead_level, storage_location_site_id, storage_location_unit_id, storage_location_floor_id, storage_location_store_id, storage_location_shelf_id, date_added, requested_by_user, added_by_user, is_active, hs_code, image_url, approval_status, approved_by, approved_date, remarks, request_date_time, material_type, measurement, origin, is_sku_system_generated, sku, is_updated, last_audit_date, last_audit_updated_by) VALUES (:id, :title, :short_title, :description, :spec1, :spec2, :spec3, :spec4, :spec5, :brand, :year, :model, :unit_id, :company_id, :function_id, :department_id, :section_id, :type_id, :category_id, :subcategory_one_id, :subcategory_two_id, :code, :item_request_id, :minimum_level, :dead_level, :storage_location_site_id, :storage_location_unit_id, :storage_location_floor_id, :storage_location_store_id, :storage_location_shelf_id, :date_added, :requested_by_user, :added_by_user, :is_active, :hs_code, :image_url, :approval_status, :approved_by, :approved_date, :remarks, :request_date_time, :material_type, :measurement, :origin, :is_sku_system_generated, :sku, :is_updated, :last_audit_date, :last_audit_updated_by) ON DUPLICATE KEY UPDATE title = :title, short_title = :short_title, description = :description, spec1 = :spec1, spec2 = :spec2, spec3 = :spec3, spec4 = :spec4, spec5 = :spec5, brand = :brand, year = :year, model = :model, unit_id = :unit_id, company_id = :company_id, function_id = :function_id, department_id = :department_id, section_id = :section_id, type_id = :type_id, category_id = :category_id, subcategory_one_id = :subcategory_one_id, subcategory_two_id = :subcategory_two_id, code = :code, item_request_id = :item_request_id, minimum_level = :minimum_level, dead_level = :dead_level, storage_location_site_id = :storage_location_site_id, storage_location_unit_id = :storage_location_unit_id, storage_location_floor_id = :storage_location_floor_id, storage_location_store_id = :storage_location_store_id, storage_location_shelf_id = :storage_location_shelf_id, date_added = :date_added, requested_by_user = :requested_by_user, added_by_user = :added_by_user, is_active = :is_active, hs_code = :hs_code, image_url = :image_url, approval_status = :approval_status, approved_by = :approved_by, approved_date = :approved_date, remarks = :remarks, request_date_time = :request_date_time, material_type = :material_type, measurement = :measurement, origin = :origin, is_sku_system_generated = :is_sku_system_generated, sku = :sku, is_updated = :is_updated, last_audit_date = :last_audit_date, last_audit_updated_by = :last_audit_updated_by", TABLE_NAME);

    private final String SELECT_LIKE_TITLE_AND_SITE_ID_BY_POSITION_CATEGORIES_QUERY = String.format(
            "SELECT * FROM %s WHERE title LIKE :title AND storage_location_site_id =:storage_location_site_id AND (category_id IN (:position_category_ids) OR subcategory_one_id IN (:position_category_ids)) ORDER BY title ASC",
            TABLE_NAME);

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

    // prepare query params
    private MapSqlParameterSource prepareInsertQueryParams(Item item) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("id", item.getId())
                .addValue("title", item.getTitle())
                .addValue("short_title", item.getShortTitle())
                .addValue("description", item.getDescription())
                .addValue("spec1", item.getSpec1())
                .addValue("spec2", item.getSpec2())
                .addValue("spec3", item.getSpec3())
                .addValue("spec4", item.getSpec4())
                .addValue("spec5", item.getSpec5())
                .addValue("brand", item.getBrand())
                .addValue("year", item.getYear())
                .addValue("model", item.getModel())
                .addValue("unit_id", item.getUnitId())
                .addValue("company_id", item.getCompanyId())
                .addValue("function_id", item.getFunctionId())
                .addValue("department_id", item.getDepartmentId())
                .addValue("section_id", item.getSectionId())
                .addValue("type_id", item.getTypeId())
                .addValue("category_id", item.getCategoryId())
                .addValue("subcategory_one_id", item.getSubcategoryOneId())
                .addValue("subcategory_two_id", item.getSubcategoryTwoId())
                .addValue("code", item.getCode())
                .addValue("item_request_id", item.getItemRequestId())
                .addValue("minimum_level", item.getMinimumLevel())
                .addValue("dead_level", item.getDeadLevel())
                .addValue("storage_location_site_id", item.getStorageLocationSiteId())
                .addValue("storage_location_unit_id", item.getStorageLocationUnitId())
                .addValue("storage_location_floor_id", item.getStorageLocationFloorId())
                .addValue("storage_location_store_id", item.getStorageLocationStoreId())
                .addValue("storage_location_shelf_id", item.getStorageLocationShelfId())
                .addValue("date_added", item.getDateAdded())
                .addValue("requested_by_user", item.getRequestedByUser())
                .addValue("added_by_user", item.getAddedByUser())
                .addValue("is_active", item.getIsActive())
                .addValue("hs_code", item.getHsCode())
                .addValue("image_url", item.getImageUrl())
                .addValue("approval_status", item.getApprovalStatus())
                .addValue("approved_by", item.getApprovedBy())
                .addValue("approved_date", item.getApprovedDate())
                .addValue("remarks", item.getRemarks())
                .addValue("request_date_time", item.getRequestDateTime())
                .addValue("material_type", item.getMaterialType())
                .addValue("measurement", item.getMeasurement())
                .addValue("origin", item.getOrigin())
                .addValue("is_sku_system_generated", item.getIsSkuSystemGenerated())
                .addValue("is_updated", item.getIsUpdated())
                .addValue("sku", item.getSku())
                .addValue("last_audit_date", item.getLastAuditDate())
                .addValue("last_audit_updated_by", item.getLastAuditUpdateBy());
        return params;
    }

    // find
    public Item find(long id) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("id", id);
        List<Item> items = namedParameterJdbcTemplate.query(SELECT_QUERY, params, new ItemRowMapper());
        if (items.size() > 0) {
            return items.get(0);
        } else {
            return new Item();
        }
    }

    // find not in
    public List<Item> findNotIn(List<Long> ids) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("ids", ids);
        return namedParameterJdbcTemplate.query(SELECT_NOT_IN_IDS, params, new ItemRowMapper());
    }

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

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

    // find by type
    public List<Item> findByTypeId(long typeId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("type_id", typeId);
        return namedParameterJdbcTemplate.query(SELECT_BY_TYPE_QUERY, params, new ItemRowMapper());
    }

    // find by category
    public List<Item> findByCategoryId(long categoryId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("category_id", categoryId);
        params.addValue("subcategory_one_id", categoryId);
        params.addValue("subcategory_two_id", categoryId);
        return namedParameterJdbcTemplate.query(SELECT_BY_CATEGORY_QUERY, params, new ItemRowMapper());
    }

    // find by category ids
    public List<Item> findByCategoryIds(List<Long> categoryIds) {
        if (!categoryIds.isEmpty()) {
            Map params = Collections.singletonMap("category_ids", categoryIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_CATEGORY_IDS, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find by position category ids
    public List<Item> findByByPositionCategories(List<Long> positionCategoryIds) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("position_category_ids", positionCategoryIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_POSITION_CATEGORIES_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find by position category ids with row limit
    public List<Item> findByByPositionCategoriesRowLimit(List<Long> positionCategoryIds, long rowLimit) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("no_rows", rowLimit);
            return namedParameterJdbcTemplate.query(SELECT_BY_POSITION_CATEGORIES_QUERY_TOP_N_ROWS, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find by position category ids and item request id with row limit
    public List<Item> findByByPositionCategoriesAndItemRequestIdRowLimit(List<Long> positionCategoryIds, long rowLimit) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("no_rows", rowLimit);
            return namedParameterJdbcTemplate.query(SELECT_BY_POSITION_CATEGORIES_AND_ITEM_REQUEST_ID_QUERY_TOP_N_ROWS, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }


    // find by department
    public List<Item> findByCategoryAndDepartmentId(long categoryId, long department) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("department_id", department);
        params.addValue("category_id", categoryId);
        params.addValue("subcategory_one_id", categoryId);
        params.addValue("subcategory_two_id", categoryId);
        return namedParameterJdbcTemplate.query(SELECT_BY_CATEGORY_DEPARTMENT_QUERY, params, new ItemRowMapper());
    }

    // find all like title (active only)
    public List<Item> findLikeTitleAndStatus(String title, boolean active) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("title", "%" + title + "%");
        params.addValue("is_active", active);
        return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_AND_STATUS_QUERY, params, new ItemRowMapper());
    }

    // find all like title and department
    public List<Item> findLikeTitleAndDepartmentId(String title, long departmentId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("title", "%" + title + "%");
        params.addValue("department_id", departmentId);
        return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_BY_DEPARTMENT_QUERY, params, new ItemRowMapper());
    }

    public List<Item> findLikeTitle(String title) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("item_title", "%" + title + "%");
        return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE, params, new ItemRowMapper());
    }

    // find all like title and departmental categories
    public List<Item> findLikeTitleByPositionAuthorizedCategories(String title, List<Long> positionCategoryIds) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("title", "%" + title + "%");
            params.addValue("position_category_ids", positionCategoryIds);
            return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_BY_POSITION_CATEGORIES_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    public List<Item> findLikeTitleByPositionAuthorizedCategories(String title, List<Long> positionCategoryIds, String isActive) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("title", "%" + title + "%");
            params.addValue("sku", "%" + title + "%");
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("is_active", isActive);
            return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_SKU_BY_POSITION_CATEGORIES_AND_ACTIVE_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    public List<Item> findLikeTitleByPositionAuthorizedCategoriesAndTypeIds(String title, List<Long> positionCategoryIds, String isActive, List<Long> itemTypeIds, Long limit) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("title", "%" + title + "%");
            params.addValue("sku", "%" + title + "%");
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("is_active", isActive);
            params.addValue("type_ids", itemTypeIds);
            params.addValue("limit", limit);
            return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_SKU_BY_POSITION_CATEGORIES_AND_ACTIVE_AND_TYPE_IDS_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    //find by type id and active
    public List<Item> findBySkuOrTitleAndTypeIdAndActive(String title, List<Long> itemTypeIds, String isActive) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("title", "%" + title + "%");
        params.addValue("sku", "%" + title + "%");
        params.addValue("is_active", isActive);
        params.addValue("type_ids", itemTypeIds);
        return namedParameterJdbcTemplate.query(SELECT_BY_SKU_TITLE_TYPE_QUERY_AND_IS_ACTIVE, params, new ItemRowMapper());
    }

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

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

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

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

    // find by position category ids with row limit and item ids
    public List<Item> findByByPositionCategoriesAndItemIdsRowLimit(List<Long> positionCategoryIds, List<Long> itemIds) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("ids", itemIds);
            return namedParameterJdbcTemplate.query(SELECT_BY_POSITION_CATEGORIES_AND_ITEM_IDS_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find all equals title
    public Item findEqualsSkuAndActiveOrNot(String sku, boolean active) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("sku", sku);
        params.addValue("is_active", active);
        List<Item> items = namedParameterJdbcTemplate.query(SELECT_EQUALS_SKU_QUERY, params, new ItemRowMapper());
        if (items.size() > 0) {
            return items.get(0);
        } else {
            return null;
        }
    }

    // find all equals title
    public List<Item> findEqualsINSkus(List<String> skus, boolean active) {
        if (skus.size() > 0) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("skus", skus);
            params.addValue("is_active", active);
            List<Item> items = namedParameterJdbcTemplate.query(SELECT_EQUALS_IN_SKU_QUERY, params, new ItemRowMapper());
            if (items.size() > 0) {
                return items;
            } else {
                return new ArrayList<>();
            }
        } else {
            return new ArrayList<>();
        }

    }

    // find By Title and SiteId
    public List<Item> findLikeTitleByPositionAuthorizedCategoriesAndSiteId(String title, List<Long> positionCategoryIds, Long siteId) {
        if (positionCategoryIds != null && !positionCategoryIds.isEmpty()) {
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("title", "%" + title + "%");
            params.addValue("position_category_ids", positionCategoryIds);
            params.addValue("storage_location_site_id", siteId);
            return namedParameterJdbcTemplate.query(SELECT_LIKE_TITLE_AND_SITE_ID_BY_POSITION_CATEGORIES_QUERY, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }

    // find only active Items
    public List<Item> findItemsActiveOnly(Collection<Long> ids) {
        if (!ids.isEmpty()) {
            Map params = Collections.singletonMap("ids", ids);
            return namedParameterJdbcTemplate.query(SELECT_BY_IDS_AND_ACTIVE, params, new ItemRowMapper());
        } else {
            return new ArrayList<>();
        }
    }
}