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.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
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 BusinessLogUserLoginMultiService extends BusinessLogServiceBase {


    public BusinessLogUserLoginMultiService(@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(AdminConstants.MONGO_DB_KEY_LOGIN_TIME,
                                new Document("$let",
                                        new Document("vars",
                                                new Document("found",
                                                        new Document("$regexFind",
                                                                new Document("input", "$message")
                                                                        .append("regex", "\"LoginTime\":\"([^\"]+)\"")
                                                        )
                                                )
                                        ).append("in",
                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                        )
                                )
                        ).append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME,
                                new Document("$let",
                                        new Document("vars",
                                                new Document("found",
                                                        new Document("$regexFind",
                                                                new Document("input", "$message")
                                                                        .append("regex", "\"LogoutTime\":\"([^\"]+)\"")
                                                        )
                                                )
                                        ).append("in",
                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                        )
                                )
                        ).append(AdminConstants.MONGO_DB_KEY_IP,
                                new Document("$let",
                                        new Document("vars",
                                                new Document("found",
                                                        new Document("$regexFind",
                                                                new Document("input", "$message")
                                                                        .append("regex", "\"Ip\":\"([^\"]+)\"")
                                                        )
                                                )
                                        ).append("in",
                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                        )
                                )
                        )
                )
        );

        operations.add(context ->
                new Document("$match",
                        new Document("$expr",
                                new Document("$and", Arrays.asList(
                                        new Document("$not",
                                                new Document("$regexMatch",
                                                        new Document("input", new Document("$toString", "$loginTime"))
                                                                .append("regex", "^(0001|1970|3000)")
                                                )
                                        ),
                                        new Document("$not",
                                                new Document("$regexMatch",
                                                        new Document("input", new Document("$toString", "$serverType"))
                                                                .append("regex", "^Login")
                                                )
                                        )
                                ))
                        )
                )
        );

        SortOperation sortOperation = Aggregation.sort(
                Sort.by(
                        Sort.Order.asc(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID),
                        Sort.Order.asc(AdminConstants.MONGO_DB_KEY_LOGTIME)
                )
        );
        operations.add(sortOperation);

        operations.add(context ->
                new Document("$group",
                        new Document("_id",
                                new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, "$accountId")
                                        .append(AdminConstants.MONGO_DB_KEY_LOGDAY, "$logDay")
                        )
                                .append(AdminConstants.MONGO_DB_KEY_LOGDAY, new Document("$first", "$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("$max", "$userNickname"))
                                .append("actions", new Document("$push",
                                        new Document(AdminConstants.MONGO_DB_KEY_ACTION, "$action")
                                                .append(AdminConstants.MONGO_DB_KEY_SERVER_TYPE, "$serverType")
                                                .append(AdminConstants.MONGO_DB_KEY_LOGTIME, "$logTime")
                                                .append(AdminConstants.MONGO_DB_KEY_LOGIN_TIME, "$loginTime")
                                                .append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME, "$logoutTime")
                                                .append(AdminConstants.MONGO_DB_KEY_IP, "$ip")
                                                .append(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE, "$languageType")
                                                .append(AdminConstants.MONGO_DB_KEY_TRAN_ID, "$tranId")
                                ))
                )
        );

        operations.add(context ->
                new Document("$addFields",
                        new Document("sessions",
                                new Document("$reduce",
                                        new Document("input", "$actions")
                                                .append("initialValue",
                                                        new Document("sessions", Arrays.asList())
                                                                .append("currentLogin", null)
                                                                .append("isProcessing", false)
                                                )
                                                .append("in",
                                                        new Document("$let",
                                                                new Document("vars",
                                                                        new Document("currentAction", "$$this")
                                                                                .append("state", "$$value")
                                                                )
                                                                        .append("in",
                                                                                new Document("$cond", Arrays.asList(
                                                                                        new Document("$eq", Arrays.asList("$$currentAction.action", "LoginToGame")),
                                                                                        new Document("sessions", "$$state.sessions")
                                                                                                .append("currentLogin", "$$currentAction")
                                                                                                .append("isProcessing", true),
                                                                                        new Document("$cond", Arrays.asList(
                                                                                                "$$state.isProcessing",
                                                                                                new Document("sessions",
                                                                                                        new Document("$concatArrays", Arrays.asList(
                                                                                                                "$$state.sessions",
                                                                                                                Arrays.asList(
                                                                                                                        new Document(AdminConstants.MONGO_DB_KEY_LOGIN_TIME, "$$state.currentLogin.loginTime")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME, "$$currentAction.logoutTime")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_TRAN_ID, "$$currentAction.tranId")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_SERVER_TYPE, "$$state.currentLogin.serverType")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_IP, "$$state.currentLogin.ip")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE, "$$state.currentLogin.languageType")
                                                                                                                                .append(AdminConstants.MONGO_DB_COLLECTION_PLAYTIME,
                                                                                                                                        new Document("$divide", Arrays.asList(
                                                                                                                                                new Document("$subtract", Arrays.asList(
                                                                                                                                                        new Document("$toDate", "$$currentAction.logoutTime"),
                                                                                                                                                        new Document("$toDate", "$$state.currentLogin.loginTime")
                                                                                                                                                )),
                                                                                                                                                1000
                                                                                                                                        ))
                                                                                                                                )
                                                                                                                )
                                                                                                        ))
                                                                                                )
                                                                                                        .append("currentLogin", null)
                                                                                                        .append("isProcessing", false),
                                                                                                new Document("sessions",
                                                                                                        new Document("$concatArrays", Arrays.asList(
                                                                                                                "$$state.sessions",
                                                                                                                Arrays.asList(
                                                                                                                        new Document(AdminConstants.MONGO_DB_KEY_LOGIN_TIME, "$$currentAction.loginTime")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME, "$$currentAction.logoutTime")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_TRAN_ID, "$$currentAction.tranId")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_SERVER_TYPE, "$$currentAction.serverType")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_IP, "$$currentAction.ip")
                                                                                                                                .append(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE, "$$currentAction.languageType")
                                                                                                                                .append(AdminConstants.MONGO_DB_COLLECTION_PLAYTIME,
                                                                                                                                        new Document("$divide", Arrays.asList(
                                                                                                                                                new Document("$subtract", Arrays.asList(
                                                                                                                                                        new Document("$toDate", "$$currentAction.logoutTime"),
                                                                                                                                                        new Document("$toDate", "$$currentAction.loginTime")
                                                                                                                                                )),
                                                                                                                                                1000
                                                                                                                                        ))
                                                                                                                                )
                                                                                                                )
                                                                                                        ))
                                                                                                )
                                                                                                        .append("currentLogin", null)
                                                                                                        .append("isProcessing", false)
                                                                                        ))
                                                                                ))
                                                                        )
                                                        )
                                                )
                                )
                        )
                ));

        operations.add(context ->
                new Document("$addFields",
                        new Document("sessions", "$sessions.sessions")
                                .append("totalPlayTime", new Document("$sum", "$sessions.sessions.playtime"))
                                .append("firstLoginTime", new Document("$min", "$sessions.sessions.loginTime"))
                                .append("lastLogoutTime", new Document("$max", "$sessions.sessions.logoutTime"))
                                .append("loginCount", new Document("$size", "$sessions.sessions"))
                )
        );

        operations.add(context ->
                new Document("$match",
                        new Document("$expr",
                                new Document("$gt", Arrays.asList(
                                        new Document("$size", "$sessions"), 0
                                ))
                        )
                )
        );

        // 최종 출력 형식
        operations.add(context ->
                new Document("$project",
                        new Document(AdminConstants.MONGO_DB_KEY_LOGDAY, "$logDay")
                                .append(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, "$accountId")
                                .append(AdminConstants.MONGO_DB_KEY_USER_GUID, 1)
                                .append(AdminConstants.MONGO_DB_KEY_USER_NICKNAME, 1)
                                .append("sessions", 1)
                                .append("firstLoginTime", 1)
                                .append("lastLogoutTime", 1)
                                .append("loginCount", 1)
                                .append("totalPlayTime", 1)
                                .append("_id", 0)
                )
        );

        AggregationOptions options = AggregationOptions.builder()
                .allowDiskUse(true)
                .build();
        Aggregation aggregation = Aggregation.newAggregation(operations).withOptions(options);
        log.info("loadBusinessLogData UserLoginMulti Query: {}", aggregation);
        AggregationResults<T> results = getMongoTemplate().aggregate(aggregation, 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)
                                        ,Criteria.where(AdminConstants.MONGO_DB_KEY_MESSAGE).regex(AdminConstants.REGEX_MSG_LOGIN_TO_GAME)
                                )
                        // AccountId가 빈값인 경우 제외
                        , Criteria.where(AdminConstants.MONGO_DB_KEY_MESSAGE).not().regex("\"AccountId\"\\s*:\\s*\"\"")
                );
    }
}
