package com.utopiaindustries.dao.ctp;

import com.utopiaindustries.model.ctp.StoreItem;
import com.utopiaindustries.util.KeyHolderFunctions;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class StoreItemDao {

    private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private static final String TABLE_NAME = "store_items";

    private static final String SELECT_BY_ID = String.format("SELECT * FROM %s WHERE id = :id", TABLE_NAME);
    private static final String SELECT_ALL = String.format("SELECT * FROM %s ORDER BY id DESC", TABLE_NAME);
    private static final String DELETE_BY_ID = String.format("DELETE FROM %s WHERE id = :id", TABLE_NAME);
    private static final String SELECT_BY_JOB_CARD_ID = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME);
    private final String COUNT_TOTAL_FINISH_ITEM = String.format("SELECT COUNT(*) FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME);

    private static final String INSERT_QUERY = String.format(
            "INSERT INTO %s (" +
                    "id, item_id, sku, barcode, job_card_id, created_at, created_by, " +
                    "finish_item_id, account_id, bundle_id, reject_reason" +
                    ") VALUES (" +
                    ":id, :item_id, :sku, :barcode, :job_card_id, :created_at, :created_by, " +
                    ":finish_item_id, :account_id, :bundle_id, :reject_reason" +
                    ") ON DUPLICATE KEY UPDATE " +
                    "item_id = VALUES(item_id), sku = VALUES(sku), barcode = VALUES(barcode), " +
                    "job_card_id = VALUES(job_card_id), created_at = VALUES(created_at), created_by = VALUES(created_by), " +
                    "finish_item_id = VALUES(finish_item_id), account_id = VALUES(account_id), bundle_id = VALUES(bundle_id), reject_reason = VALUES(reject_reason)",
            TABLE_NAME
    );
    String SELECT_BY_JOB_CARD_GROUP_REJECT_REASON =  String.format("SELECT reject_reason, COUNT(*) AS total FROM %s WHERE job_card_id IN (:job_card_id) GROUP BY reject_reason", TABLE_NAME);

    private static final String SELECT_BY_DATE_AND_IDs = String.format("SELECT COUNT(*) FROM %s WHERE (:start_date IS NULL OR created_at >= :start_date) AND created_at <= :end_date AND id IN (:ids)", TABLE_NAME);

    public StoreItemDao(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    private MapSqlParameterSource prepareParams(StoreItem item) {
        return new MapSqlParameterSource()
                .addValue("id", item.getId())
                .addValue("item_id", item.getItemId())
                .addValue("sku", item.getSku())
                .addValue("barcode", item.getBarcode())
                .addValue("job_card_id", item.getJobCardId())
                .addValue("created_at", item.getCreatedAt())
                .addValue("created_by", item.getCreatedBy())
                .addValue("finish_item_id", item.getFinishedItemId())
                .addValue("account_id", item.getAccountId())
                .addValue("bundle_id", item.getBundleId())
                .addValue("reject_reason", item.getRejectedReason());
    }

    public StoreItem find(long id) {
        MapSqlParameterSource params = new MapSqlParameterSource("id", id);
        return namedParameterJdbcTemplate.query(SELECT_BY_ID, params, new StoreItemRowMapper())
                .stream().findFirst().orElse(new StoreItem());
    }

    public List<StoreItem> findAll() {
        return namedParameterJdbcTemplate.query(SELECT_ALL, new StoreItemRowMapper());
    }

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

    public int[] saveAll(List<StoreItem> items) {
        List<MapSqlParameterSource> batchParams = new ArrayList<>();
        for (StoreItem item : items) {
            batchParams.add(prepareParams(item));
        }
        return namedParameterJdbcTemplate.batchUpdate(INSERT_QUERY, batchParams.toArray(new MapSqlParameterSource[0]));
    }

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

    public List<StoreItem> findByJobCardId(long jobCardId) {
        MapSqlParameterSource params = new MapSqlParameterSource("job_card_id", jobCardId);
        return namedParameterJdbcTemplate.query(SELECT_BY_JOB_CARD_ID, params, new StoreItemRowMapper());
    }

    public Long calculateTotalRejectItemByJobCardId(long jobCardId) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("job_card_id", jobCardId);
        Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_FINISH_ITEM, params, Long.class);
        return count != null ? count : 0;
    }

    public Long findByDateAndIds(String startDate, String endDate, List<Long> ids) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("start_date", startDate);
        params.addValue("end_date", endDate);
        params.addValue("ids", ids);
        Long count = namedParameterJdbcTemplate.queryForObject(SELECT_BY_DATE_AND_IDs, params, Long.class);
        return count != null ? count : 0;
    }

    public Map<String, Integer> totalCountByJobCardIdsAndGroupByRejectReason(List<Long> jobCardIds) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("job_card_id", jobCardIds);

        List<Map<String, Object>> rows = namedParameterJdbcTemplate.queryForList(SELECT_BY_JOB_CARD_GROUP_REJECT_REASON, params);

        Map<String, Integer> result = new HashMap<>();
        for (Map<String, Object> row : rows) {
            String reason = (String) row.get("reject_reason");
            Integer total = ((Number) row.get("total")).intValue();
            result.put(reason, total);
        }
        return result;
    }

}
