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.*;
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 BusinessLogPlayTimeService extends BusinessLogServiceBase {

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

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

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

            operations.add(Aggregation.match(Criteria.where(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID).ne(null)));

            //3000이라는 값이 들어있는 경우가 있어서 예외처리
            operations.add(context ->
                new Document("$match",
                    new Document(AdminConstants.MONGO_DB_KEY_LOGIN_TIME,
                        new Document("$not",
                            new Document("$regex", "^3000")
                        )
                    )
                )
            );

            // 로그인서버는 제외
            operations.add(context ->
                new Document("$match",
                    new Document(AdminConstants.MONGO_DB_KEY_SERVER_TYPE,
                        new Document("$ne", "Login")
                    )
                )
            );

            // 유저, 시간순으로 정렬
            operations.add(context ->
                new Document("$sort",
                    new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, 1)
                        .append(AdminConstants.MONGO_DB_KEY_LOGTIME, 1)
                )
            );

            operations.add(context ->
                new Document("$project",
                    new Document("accountId", 1)
                        .append("logTime", 1)
                        .append("loginTime", 1)
                        .append("logoutTime", 1)
                        .append("action", 1)
                        .append("message", 1)
                        .append("logDay", 1)
                )
            );

            operations.add(context ->
                new Document("$group",
                    new Document("_id",
                        new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, "$accountId")
                            .append("logDay", "$logDay")
                    )
                        .append("actions",
                            new Document("$push",
                                new Document(AdminConstants.MONGO_DB_KEY_ACTION, "$action")
                                    .append(AdminConstants.MONGO_DB_KEY_LOGTIME, "$logTime")
                                    .append(AdminConstants.MONGO_DB_KEY_LOGIN_TIME, "$loginTime")
                                    .append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME, "$logoutTime")
                            )
                        )
                ));

            //
            operations.add(context ->
                new Document("$project",
                    new Document("_id", 1)
                        .append("sessions",
                            new Document("$map",
                                new Document("input", "$actions")
                                    .append("as", "action")
                                    .append("in",
                                        new Document("$cond", new Document()
                                            .append("if", new Document("$eq", Arrays.asList("$$action.action", "UserLogout")))
                                            .append("then",
                                                new Document("$let", new Document()
                                                    .append("vars",
                                                        new Document("prevIndex",
                                                            new Document("$subtract", Arrays.asList(
                                                                new Document("$indexOfArray", Arrays.asList("$actions", "$$action")),
                                                                1
                                                            ))
                                                        )
                                                    )
                                                    .append("in",
                                                        new Document("$cond", new Document()
                                                            .append("if",
                                                                new Document("$and", Arrays.asList(
                                                                    new Document("$gte", Arrays.asList("$$prevIndex", 0)),
                                                                    new Document("$eq", Arrays.asList(
                                                                        new Document("$arrayElemAt", Arrays.asList("$actions.action", "$$prevIndex")),
                                                                        "LoginToGame"
                                                                    ))
                                                                ))
                                                            )
                                                            .append("then",
                                                                new Document()
                                                                    .append("type", "paired")
                                                                    .append("login", new Document("$arrayElemAt", Arrays.asList("$actions", "$$prevIndex")))
                                                                    .append("logout", "$$action")
                                                            )
                                                            .append("else",
                                                                new Document()
                                                                    .append("type", "single")
                                                                    .append("logout", "$$action")
                                                            )
                                                        )
                                                    )
                                                )
                                            )
                                            .append("else", null)
                                        )
                                    )
                            )
                        )
                )
            );

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

            operations.add(context ->
                new Document("$match",
                    new Document("$and", Arrays.asList(
                        new Document("sessions", new Document("$ne", null)),
                        new Document("$or", Arrays.asList(
                            new Document("sessions.type", "paired"),
                            new Document("$and", Arrays.asList(
                                new Document("sessions.type", "single"),
                                new Document("sessions.logout.loginTime",
                                    new Document("$not",
                                        new Document("$regex", "^0001")
                                    )
                                )
                            ))
                        ))
                    ))
                )
            );

            operations.add(context ->
                new Document("$project",
                    new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, "$_id")
                        .append("logDay", 1)
                        .append("sessionType", "$sessions.type")
                        .append("loginTime",
                            new Document("$cond",
                                new Document("if", new Document("$eq", Arrays.asList("$sessions.type", "paired")))
                                    .append("then", "$sessions.login.loginTime")
                                    .append("else", "$sessions.logout.loginTime")
                            )
                        )
                        .append("logoutTime",
                            new Document("$cond",
                                new Document("if", new Document("$eq", Arrays.asList("$sessions.type", "paired")))
                                    .append("then", "$sessions.logout.logoutTime")
                                    .append("else", "$sessions.logout.logoutTime")
                            )
                        )
                        .append("logTime",
                            new Document("$cond",
                                new Document("if", new Document("$eq", Arrays.asList("$sessions.type", "paired")))
                                    .append("then", "$sessions.logout.logTime")
                                    .append("else", "$sessions.logout.logTime")
                            )
                        )
                )
            );

            operations.add(context ->
                new Document("$addFields",
                    new Document("playTimeSeconds",
                        new Document("$divide", Arrays.asList(
                            new Document("$dateDiff",
                                new Document("startDate",
                                    new Document("$dateFromString",
                                        new Document("dateString",
                                            new Document("$substr", Arrays.asList("$loginTime", 0, 19))
                                        )
                                            .append("format", "%Y-%m-%dT%H:%M:%S")
                                    )
                                )
                                    .append("endDate",
                                        new Document("$dateFromString",
                                            new Document("dateString",
                                                new Document("$substr", Arrays.asList("$logoutTime", 0, 19))
                                            )
                                                .append("format", "%Y-%m-%dT%H:%M:%S")
                                        )
                                    )
                                    .append("unit", "millisecond")
                            ),
                            1000
                        ))
                    )
                )
            );

            operations.add(context ->
                new Document("$group",
                    new Document("_id", "$_id.logDay")
                        .append("logDay",
                            new Document("$first", "$_id.logDay")
                        )
                        .append("totalPlayTimeCount",
                            new Document("$sum", "$playTimeSeconds")
                        )
                ));

            Aggregation aggregation = Aggregation.newAggregation(operations);
            log.info("loadBusinessLogData PlayTime Query: {}", aggregation);
            AggregationResults<T> results = getMongoTemplate()
                    .aggregate(aggregation, AdminConstants.MONGO_DB_COLLECTION_LOG, class1);

            return results.getMappedResults();
        }catch (Exception e) {
            log.error("loadBusinessLogData Error Message: {}", e.getMessage());
            return null;
        }
    }

//    public <T> List<T> loadBusinessLogData(String startTime, String endTime, Class<T> class1) {
//        try {
//
//            Criteria criteria = makeCriteria(startTime, endTime);
//            List<AggregationOperation> operations = setDefaultOperation(criteria);
//
//            operations.add(Aggregation.match(Criteria.where(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID).ne(null)));
//
//            //3000이라는 값이 들어있는 경우가 있어서 예외처리
//            operations.add(context ->
//                    new Document("$match",
//                            new Document(AdminConstants.MONGO_DB_KEY_LOGIN_TIME,
//                                    new Document("$not",
//                                            new Document("$regex", "^3000")
//                                    )
//                            )
//                    )
//            );
//
//            //playtime 포맷
//            operations.add(context ->
//                    new Document("$addFields",
//                            new Document("LoginTimeDate",
//                                    new Document("$dateFromString",
//                                            new Document("dateString",
//                                                    new Document("$substr", List.of("$loginTime", 0, 19))
//                                            ).append("format", "%Y-%m-%dT%H:%M:%S")
//                                    )
//                            )
//                                    .append("LogoutTimeDate",
//                                            new Document("$dateFromString",
//                                                    new Document("dateString",
//                                                            new Document("$substr", List.of("$logoutTime", 0, 19))
//                                                    ).append("format", "%Y-%m-%dT%H:%M:%S")
//                                            )
//                                    )
//                    ));
//
//            operations.add(context ->
//                    new Document("$addFields",
//                            new Document("PlayTimeInSeconds",
//                                    new Document("$floor",  // 소수점 버림
//                                            new Document("$divide", List.of(
//                                                    new Document("$subtract", List.of("$LogoutTimeDate", "$LoginTimeDate")),
//                                                    1000
//                                            ))
//                                    )
//                            )
//                    ));
//
//            //그룹화
//            operations.add(context ->
//                    new Document("$group",
//                            new Document("_id",
//                                    new Document("accountId", "$accountId")
//                                            .append("logDay", "$logDay")
//                            )
//                                    .append("totalPlayTimeInSeconds",
//                                            new Document("$max", "$PlayTimeInSeconds")
//                                    )
//                    ));
//
//            operations.add(context ->
//                    new Document("$group",
//                            new Document("_id", "$_id.logDay")
//                                    .append("totalPlayTimeAcrossAccountsInSeconds",
//                                            new Document("$sum", "$totalPlayTimeInSeconds")
//                                    )
//                    ));
//            operations.add(context ->
//                    new Document("$project",
//                            new Document("_id", 0)
//                                    .append(AdminConstants.MONGO_DB_KEY_LOGDAY, "$_id")
//                                    .append("totalPlayTimeCount",
//                                            new Document("$toString", "$totalPlayTimeAcrossAccountsInSeconds")
//                                    )
//                    ));
//
//            Aggregation aggregation = Aggregation.newAggregation(operations);
//            log.info("loadBusinessLogData PlayTime Query: {}", aggregation);
//            AggregationResults<T> results = getMongoTemplate()
//                    .aggregate(aggregation, AdminConstants.MONGO_DB_COLLECTION_LOG, class1);
//
//            return results.getMappedResults();
//        }catch (Exception e) {
//            log.error("loadBusinessLogData Error Message: {}", e.getMessage());
//            return null;
//        }
//    }

    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_PLAY_TIME),
                                        Criteria.where(AdminConstants.MONGO_DB_KEY_MESSAGE).regex(AdminConstants.REGEX_MSG_LOGIN_TO_GAME)
                                )
                );
    }
}
