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 BusinessLogSnapshotService extends BusinessLogServiceBase {

    public BusinessLogSnapshotService(@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("$addFields",
                        new Document("gold",
                                new Document("$toDouble",
                                        new Document("$arrayElemAt", Arrays.asList(
                                                new Document("$ifNull", Arrays.asList(
                                                        new Document("$getField",
                                                                new Document("field", "captures")
                                                                        .append("input",
                                                                                new Document("$regexFind",
                                                                                        new Document("input", "$message")
                                                                                                .append("regex", "\"Gold\":([0-9.]+)")
                                                                                )
                                                                        )
                                                        ),
                                                        Arrays.asList()
                                                )),
                                                0
                                        ))
                                )
                        )
                                .append("sapphire",
                                        new Document("$toDouble",
                                                new Document("$arrayElemAt", Arrays.asList(
                                                        new Document("$ifNull", Arrays.asList(
                                                                new Document("$getField",
                                                                        new Document("field", "captures")
                                                                                .append("input",
                                                                                        new Document("$regexFind",
                                                                                                new Document("input", "$message")
                                                                                                        .append("regex", "\"Sapphire\":([0-9.]+)")
                                                                                        )
                                                                                )
                                                                ),
                                                                Arrays.asList()
                                                        )),
                                                        0
                                                ))
                                        )
                                )
                                .append("calium",
                                        new Document("$toDouble",
                                                new Document("$arrayElemAt", Arrays.asList(
                                                        new Document("$ifNull", Arrays.asList(
                                                                new Document("$getField",
                                                                        new Document("field", "captures")
                                                                                .append("input",
                                                                                        new Document("$regexFind",
                                                                                                new Document("input", "$message")
                                                                                                        .append("regex", "\"Calium\":([0-9.]+)")
                                                                                        )
                                                                                )
                                                                ),
                                                                Arrays.asList()
                                                        )),
                                                        0
                                                ))
                                        )
                                )
                                .append("ruby",
                                        new Document("$toDouble",
                                                new Document("$arrayElemAt", Arrays.asList(
                                                        new Document("$ifNull", Arrays.asList(
                                                                new Document("$getField",
                                                                        new Document("field", "captures")
                                                                                .append("input",
                                                                                        new Document("$regexFind",
                                                                                                new Document("input", "$message")
                                                                                                        .append("regex", "\"Ruby\":([0-9.]+)")
                                                                                        )
                                                                                )
                                                                ),
                                                                Arrays.asList()
                                                        )),
                                                        0
                                                ))
                                        )
                                )
                                .append("itemsString",
                                        new Document("$regexFind",
                                                new Document("input", "$message")
                                                        .append("regex", "\"items\":\\s*(\\[.*?\\])")
                                                        .append("options", "s")
                                        )
                                )
                                .append("snapshotDatetime",
                                        new Document("$switch",
                                                new Document("branches", Arrays.asList(
                                                        new Document("case",
                                                                new Document("$eq", Arrays.asList(
                                                                        new Document("$type", "$logTime"),
                                                                        "date"
                                                                ))
                                                        ).append("then", "$logTime"),
                                                        new Document("case",
                                                                new Document("$eq", Arrays.asList(
                                                                        new Document("$type", "$logTime"),
                                                                        "string"
                                                                ))
                                                        ).append("then",
                                                                new Document("$dateFromString",
                                                                        new Document("dateString", "$logTime")
                                                                                .append("onError", "$NOW")
                                                                )
                                                        )
                                                ))
                                                        .append("default", "$NOW")
                                        )
                                )
                )
        );

        operations.add(context ->
                new Document("$addFields",
                        new Document("items",
                                new Document("$cond",
                                        new Document("if",
                                                new Document("$ne", Arrays.asList("$itemsString", null))
                                        )
                                                .append("then",
                                                        new Document("$map",
                                                                new Document("input",
                                                                        new Document("$split", Arrays.asList(
                                                                                new Document("$arrayElemAt", Arrays.asList("$itemsString.captures", 0)),
                                                                                "},{"
                                                                        ))
                                                                )
                                                                        .append("as", "item")
                                                                        .append("in",
                                                                                new Document("$let",
                                                                                        new Document("vars",
                                                                                                new Document("cleanItem",
                                                                                                        new Document("$replaceAll",
                                                                                                                new Document("input",
                                                                                                                        new Document("$replaceAll",
                                                                                                                                new Document("input",
                                                                                                                                        new Document("$replaceAll",
                                                                                                                                                new Document("input", "$$item")
                                                                                                                                                        .append("find", "[")
                                                                                                                                                        .append("replacement", "")
                                                                                                                                        )
                                                                                                                                )
                                                                                                                                        .append("find", "]")
                                                                                                                                        .append("replacement", "")
                                                                                                                        )
                                                                                                                )
                                                                                                                        .append("find", "{")
                                                                                                                        .append("replacement", "")
                                                                                                        )
                                                                                                )
                                                                                        )
                                                                                                .append("in",
                                                                                                        new Document("$let",
                                                                                                                new Document("vars",
                                                                                                                        new Document("itemParts",
                                                                                                                                new Document("$split", Arrays.asList("$$cleanItem", ","))
                                                                                                                        )
                                                                                                                )
                                                                                                                        .append("in",
                                                                                                                                new Document("ItemMetaId",
                                                                                                                                        new Document("$toInt",
                                                                                                                                                new Document("$trim",
                                                                                                                                                        new Document("input",
                                                                                                                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                                                                                                                        new Document("$split", Arrays.asList(
                                                                                                                                                                                new Document("$arrayElemAt", Arrays.asList("$$itemParts", 0)),
                                                                                                                                                                                ":"
                                                                                                                                                                        )),
                                                                                                                                                                        1
                                                                                                                                                                ))
                                                                                                                                                        )
                                                                                                                                                )
                                                                                                                                        )
                                                                                                                                )
                                                                                                                                        .append("ItemStackCount",
                                                                                                                                                new Document("$toInt",
                                                                                                                                                        new Document("$trim",
                                                                                                                                                                new Document("input",
                                                                                                                                                                        new Document("$replaceAll",
                                                                                                                                                                                new Document("input",
                                                                                                                                                                                        new Document("$arrayElemAt", Arrays.asList(
                                                                                                                                                                                                new Document("$split", Arrays.asList(
                                                                                                                                                                                                        new Document("$arrayElemAt", Arrays.asList("$$itemParts", 1)),
                                                                                                                                                                                                        ":"
                                                                                                                                                                                                )),
                                                                                                                                                                                                1
                                                                                                                                                                                        ))
                                                                                                                                                                                )
                                                                                                                                                                                        .append("find", "}")
                                                                                                                                                                                        .append("replacement", "")
                                                                                                                                                                        )
                                                                                                                                                                )
                                                                                                                                                        )
                                                                                                                                                )
                                                                                                                                        )
                                                                                                                        )
                                                                                                        )
                                                                                                )
                                                                                )
                                                                        )
                                                        )
                                                )
                                                .append("else", Arrays.asList())
                                )
                        )
                )
        );

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

        operations.add(context ->
                new Document("$group",
                        new Document("_id",
                                new Document("userGuid", "$userGuid")
                                        .append("date", "$logDay")
                        )
                                .append(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, new Document("$first", "$accountId"))
                                .append(AdminConstants.MONGO_DB_KEY_USER_GUID, new Document("$first", "$userGuid"))
                                .append(AdminConstants.MONGO_DB_KEY_USER_NICKNAME, new Document("$first", "$userNickname"))
                                .append("gold", new Document("$first", "$gold"))
                                .append("sapphire", new Document("$first", "$sapphire"))
                                .append("calium", new Document("$first", "$calium"))
                                .append("ruby", new Document("$first", "$ruby"))
                                .append("items", new Document("$first", "$items"))
                                .append("snapshotDatetime", new Document("$first", "$snapshotDatetime"))
                )
        );

        operations.add(context ->
                new Document("$project",
                        new Document("_id", 0)
                                .append(AdminConstants.MONGO_DB_KEY_USER_GUID, "$userGuid")
                                .append(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, "$accountId")
                                .append(AdminConstants.MONGO_DB_KEY_USER_NICKNAME, "$userNickname")
                                .append("gold", new Document("$ifNull", Arrays.asList("$gold", 0)))
                                .append("sapphire", new Document("$ifNull", Arrays.asList("$sapphire", 0)))
                                .append("calium", new Document("$ifNull", Arrays.asList("$calium", 0)))
                                .append("ruby", new Document("$ifNull", Arrays.asList("$ruby", 0)))
                                // .append("items", "$items")
                                .append("item_11570001",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 11570001))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_11570002",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 11570002))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080002",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080002))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080004",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080004))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080005",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080005))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080006",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080006))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080007",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080007))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080008",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080008))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("item_13080009",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("foundItem",
                                                                new Document("$arrayElemAt", Arrays.asList(
                                                                        new Document("$filter",
                                                                                new Document("input", "$items")
                                                                                        .append("cond",
                                                                                                new Document("$eq", Arrays.asList("$$this.ItemMetaId", 13080009))
                                                                                        )
                                                                        ),
                                                                        0
                                                                ))
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$ifNull", Arrays.asList("$$foundItem.ItemStackCount", 0))
                                                        )
                                        )
                                )
                                .append("lastLogoutTime", "$snapshotDatetime")
                )
        );

        Aggregation aggregation = Aggregation.newAggregation(operations);
        log.info("loadBusinessLogData Snapshot 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)
                        , new Criteria()
                                .orOperator(
                                        Criteria.where(AdminConstants.MONGO_DB_KEY_MESSAGE).regex(AdminConstants.REGEX_MSG_USER_LOGOUT_SNAPSHOT)
                                )
                );
    }
}
