package com.caliverse.admin.logs.logservice.businesslogservice;

import com.caliverse.admin.global.common.constants.AdminConstants;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

@Slf4j
@Service
public class BusinessLogItemService extends BusinessLogServiceBase {

    public BusinessLogItemService(@Qualifier("mongoBusinessLogTemplate") MongoTemplate mongoTemplate) {
        super(mongoTemplate);
    }

    public <T> List<T> loadBusinessLogData(String startTime, String endTime, Class<T> class1) {

        Criteria criteria = makeCriteria(startTime, endTime);
        List<AggregationOperation> operations = setDefaultOperation(criteria);

        operations.add(context ->
                new Document("$group",
                        new Document("_id", "$tranId")
                                .append("logDay", new Document("$first", "$logDay"))
                                .append("logTime", new Document("$first", "$logTime"))
                                .append("userGuid", new Document("$first", "$userGuid"))
                                .append("userNickname", new Document("$first", "$userNickname"))
                                .append("accountId", new Document("$first", "$accountId"))
                                .append("action", new Document("$first", "$action"))
                                .append("documents", new Document("$push", "$$ROOT"))
                )
        );

        operations.add(context ->
                new Document("$match",
                        new Document("documents",
                                new Document("$elemMatch",
                                        new Document("message",
                                                new Document("$regex", "\"Domain\":\"Item\"")
                                        )
                                )
                        )
                )
        );

        operations.add(context ->
                new Document("$sort",
                        new Document("logTime", 1)
                )
        );

        operations.add(context ->
                new Document("$addFields",
                        new Document("itemDocuments",
                                new Document("$filter",
                                        new Document("input", "$documents")
                                                .append("as", "doc")
                                                .append("cond",
                                                        new Document("$regexMatch",
                                                                new Document("input", "$$doc.message")
                                                                        .append("regex", "\"Domain\":\"Item\"")
                                                        )
                                                )
                                )
                        )
                )
        );

        operations.add(context ->
                new Document("$addFields",
                        new Document("itemInfo",
                                new Document("$map",
                                        new Document("input", "$itemDocuments")
                                                .append("as", "doc")
                                                .append("in",
                                                        new Document("itemMID",
                                                                new Document("$let",
                                                                        new Document("vars",
                                                                                new Document("found",
                                                                                        new Document("$regexFind",
                                                                                                new Document("input", "$$doc.message")
                                                                                                        .append("regex", "\"ItemMID\":([^,}]+)")
                                                                                        )
                                                                                )
                                                                        )
                                                                                .append("in",
                                                                                        new Document("$toInt",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                                )
                                                                )
                                                        )
                                                                .append("itemName",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"ItemName\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("action",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"Action\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("itemTypeLarge",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"ItemTypeLarge\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("itemTypeSmall",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"ItemTypeSmall\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("countDeltaType",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"CountDeltaType\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("deltaCount",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"DeltaCount\":([^,}]+)")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$toInt",
                                                                                                        new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                                )
                                                                                        )
                                                                        )
                                                                )
                                                                .append("stackCount",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"StackCount\":([^,}]+)")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$toInt",
                                                                                                        new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                                )
                                                                                        )
                                                                        )
                                                                )
                                                                .append("itemPK",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"ItemPK\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                                .append("itemSK",
                                                                        new Document("$let",
                                                                                new Document("vars",
                                                                                        new Document("found",
                                                                                                new Document("$regexFind",
                                                                                                        new Document("input", "$$doc.message")
                                                                                                                .append("regex", "\"ItemSK\":\"([^\"]+)\"")
                                                                                                )
                                                                                        )
                                                                                )
                                                                                        .append("in",
                                                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                                                        )
                                                                        )
                                                                )
                                                )
                                )
                        )
                )
        );

        operations.add(context ->
                new Document("$unwind", "$itemInfo")
        );

        operations.add(context ->
                new Document("$match",
                        new Document("itemInfo.itemMID", new Document("$ne", null))
                                .append("itemInfo.itemMID", new Document("$ne", 0))
                                .append("itemInfo.countDeltaType", new Document("$ne", null))
                                .append("itemInfo.deltaCount", new Document("$ne", null))
                )
        );

        operations.add(context ->
                new Document("$group",
                        new Document("_id",
                                new Document("logDay", "$logDay")
                                        .append("accountId", "$accountId")
                                        .append("tranId", "$_id")
                        )
                                .append("userGuid", new Document("$first", "$userGuid"))
                                .append("userNickname", new Document("$first", "$userNickname"))
                                .append("logTime", new Document("$first", "$logTime"))
                                .append("transactionItems",
                                        new Document("$push",
                                                new Document("itemMID", "$itemInfo.itemMID")
                                                        .append("itemName", "$itemInfo.itemName")
                                                        .append("action", "$itemInfo.action")
                                                        .append("itemTypeLarge", "$itemInfo.itemTypeLarge")
                                                        .append("itemTypeSmall", "$itemInfo.itemTypeSmall")
                                                        .append("countDeltaType", "$itemInfo.countDeltaType")
                                                        .append("deltaCount", "$itemInfo.deltaCount")
                                                        .append("stackCount", "$itemInfo.stackCount")
                                                        .append("itemPK", "$itemInfo.itemPK")
                                                        .append("itemSK", "$itemInfo.itemSK")
                                        )
                                )
                )
        );

        operations.add(context ->
                new Document("$group",
                        new Document("_id",
                                new Document("logDay", "$_id.logDay")
                                        .append("accountId", "$_id.accountId")
                        )
                                .append("userGuid", new Document("$first", "$userGuid"))
                                .append("userNickname", new Document("$first", "$userNickname"))
                                .append("totalItems", new Document("$sum", 1))
                                .append("itemTypeLargeData",
                                        new Document("$push",
                                                new Document("$map",
                                                        new Document("input", "$transactionItems")
                                                                .append("as", "item")
                                                                .append("in",
                                                                        new Document("type", "$$item.itemTypeLarge")
                                                                                .append("countDeltaType", "$$item.countDeltaType")
                                                                                .append("deltaCount", "$$item.deltaCount")
                                                                )
                                                )
                                        )
                                )
                                .append("itemDetails",
                                        new Document("$push",
                                                new Document("tranId", "$_id.tranId")
                                                        .append("action", "$_id.action")
                                                        .append("logTime", "$logTime")
                                                        .append("documents", "$documents")
                                                        .append("items", "$transactionItems")
                                        )
                                )
                )
        );

        operations.add(context ->
                new Document("$project",
                        new Document("_id", 0)
                                .append("logDay", "$_id.logDay")
                                .append("accountId", "$_id.accountId")
                                .append("userGuid", 1)
                                .append("userNickname", 1)
                                .append("totalItems", 1)
                                .append("itemTypeLargeStats",
                                        new Document("$map",
                                                new Document("input",
                                                        new Document("$setUnion", Arrays.asList(
                                                                new Document("$reduce",
                                                                        new Document("input", "$itemTypeLargeData")
                                                                                .append("initialValue", Arrays.asList())
                                                                                .append("in",
                                                                                        new Document("$setUnion", Arrays.asList(
                                                                                                "$$value",
                                                                                                new Document("$map",
                                                                                                        new Document("input", "$$this")
                                                                                                                .append("as", "item")
                                                                                                                .append("in", "$$item.type")
                                                                                                )
                                                                                        ))
                                                                                )
                                                                )
                                                        ))
                                                )
                                                        .append("as", "type")
                                                        .append("in",
                                                                new Document("type", "$$type")
                                                                        .append("acquired",
                                                                                new Document("$sum",
                                                                                        new Document("$reduce",
                                                                                                new Document("input", "$itemTypeLargeData")
                                                                                                        .append("initialValue", 0)
                                                                                                        .append("in",
                                                                                                                new Document("$add", Arrays.asList(
                                                                                                                        "$$value",
                                                                                                                        new Document("$sum",
                                                                                                                                new Document("$map",
                                                                                                                                        new Document("input",
                                                                                                                                                new Document("$filter",
                                                                                                                                                        new Document("input", "$$this")
                                                                                                                                                                .append("as", "item")
                                                                                                                                                                .append("cond",
                                                                                                                                                                        new Document("$and", Arrays.asList(
                                                                                                                                                                                new Document("$eq", Arrays.asList("$$item.type", "$$type")),
                                                                                                                                                                                new Document("$eq", Arrays.asList("$$item.countDeltaType", "Acquire"))
                                                                                                                                                                        ))
                                                                                                                                                                )
                                                                                                                                                )
                                                                                                                                        )
                                                                                                                                                .append("as", "filteredItem")
                                                                                                                                                .append("in", "$$filteredItem.deltaCount")
                                                                                                                                )
                                                                                                                        )
                                                                                                                ))
                                                                                                        )
                                                                                        )
                                                                                )
                                                                        )
                                                                        .append("consumed",
                                                                                new Document("$sum",
                                                                                        new Document("$reduce",
                                                                                                new Document("input", "$itemTypeLargeData")
                                                                                                        .append("initialValue", 0)
                                                                                                        .append("in",
                                                                                                                new Document("$add", Arrays.asList(
                                                                                                                        "$$value",
                                                                                                                        new Document("$sum",
                                                                                                                                new Document("$map",
                                                                                                                                        new Document("input",
                                                                                                                                                new Document("$filter",
                                                                                                                                                        new Document("input", "$$this")
                                                                                                                                                                .append("as", "item")
                                                                                                                                                                .append("cond",
                                                                                                                                                                        new Document("$and", Arrays.asList(
                                                                                                                                                                                new Document("$eq", Arrays.asList("$$item.type", "$$type")),
                                                                                                                                                                                new Document("$eq", Arrays.asList("$$item.countDeltaType", "Consume"))
                                                                                                                                                                        ))
                                                                                                                                                                )
                                                                                                                                                )
                                                                                                                                        )
                                                                                                                                                .append("as", "filteredItem")
                                                                                                                                                .append("in", new Document("$abs", "$$filteredItem.deltaCount"))
                                                                                                                                )
                                                                                                                        )
                                                                                                                ))
                                                                                                        )
                                                                                        )
                                                                                )
                                                                        )
                                                        )
                                        )
                                )
                                .append("itemDetails", 1)
                )
        );

        operations.add(context ->
                new Document("$sort",
                        new Document("logDay", 1)
                                .append("accountId", 1)
                )
        );

        Aggregation aggregation = Aggregation.newAggregation(operations);
        log.info("loadBusinessLogData Item Query: {}", aggregation);
        AggregationResults<T> results = getMongoTemplate().aggregate(
                aggregation.withOptions(AggregationOptions.builder().allowDiskUse(true).build())
                , AdminConstants.MONGO_DB_COLLECTION_LOG
                , class1
        );

        return results.getMappedResults();
    }


    protected Criteria makeCriteria(String startTime, String endTime) {
        return new Criteria()
                .andOperator(
                        Criteria.where(AdminConstants.MONGO_DB_KEY_LOGTIME).gte(startTime)
                        ,Criteria.where(AdminConstants.MONGO_DB_KEY_LOGTIME).lt(endTime)
                );
    }
}
