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

import com.caliverse.admin.Indicators.Indicatordomain.IndicatorsLog;
import com.caliverse.admin.Indicators.Indicatorsservice.base.IndicatorsLogLoadServiceBase;
import com.caliverse.admin.Indicators.entity.UserCreateLogInfo;
import com.caliverse.admin.Indicators.indicatorrepository.IndicatorUserCreateRepository;
import com.caliverse.admin.global.common.constants.AdminConstants;
import com.caliverse.admin.logs.Indicatordomain.UserCreateMongoLog;
import com.caliverse.admin.logs.logservice.businesslogservice.BusinessLogUserCreateService;
import com.caliverse.admin.mongodb.dto.MongoPageResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
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.ArrayList;
import java.util.List;

@Slf4j
@Service
public class IndicatorsUserCreateService extends IndicatorsLogLoadServiceBase {

    @Autowired private IndicatorUserCreateRepository indicatorUserCreateRepository;
    @Autowired private BusinessLogUserCreateService userCreateService;

    public IndicatorsUserCreateService(@Qualifier("mongoIndicatorTemplate") MongoTemplate mongoTemplate) {
        super(mongoTemplate);
    }

    public void collectUserCreate(String startTime, String endTime){
        String logTimeStr = startTime.substring(0, 10);
        List<UserCreateMongoLog> indicatorsLog = userCreateService.loadBusinessLogData(startTime, endTime, UserCreateMongoLog.class);

        if (indicatorsLog == null || indicatorsLog.isEmpty()) {
            log.info("collectUserCreate indicatorsLog Log logDay: {} null", logTimeStr);
            return;
        }

        for(UserCreateMongoLog mongo_log : indicatorsLog){
            String logDay = mongo_log.getLogDay();

            UserCreateLogInfo userCreateLog = new UserCreateLogInfo(logDay, mongo_log.getAccountId(), mongo_log.getUserGuid(), mongo_log.getUserNickname(), mongo_log.getCreatedTime());
            saveStatLogData(userCreateLog);
        }
        log.info("collectUserCreate UserCreate Log Save logDay: {}", logTimeStr);
    }

    public void saveStatLogData(IndicatorsLog indicatorsLog) {

        if (indicatorsLog instanceof UserCreateLogInfo logInfo) {
            try {
                boolean exists = indicatorUserCreateRepository.existsByLogDayAndUserGuid(
                        logInfo.getLogDay(),
                        logInfo.getUserGuid()
                );
                if (exists) {
                    log.debug("Duplicate data skipped - logDay: {}, userGuid: {}",
                            logInfo.getLogDay(), logInfo.getUserGuid());
                    return;
                }
                indicatorUserCreateRepository.save(logInfo);
            } catch (Exception e) {
                log.error("Error Repository Write UserCreateLogInfo: {}, Message: {}", logInfo, e.getMessage());
            }
        } else {
            log.error("Not instanceof UserCreateLogInfo");
        }
    }

    @Override
    public <T extends IndicatorsLog> List<T> getIndicatorsLogData(String startTime, String endTime, Class<T> clazz) {
        return List.of();
    }

    public <T extends IndicatorsLog> MongoPageResult<T> getUserCreateLogData(
            String searchType,
            String searchData,
            String startTime,
            String endTime,
            String orderBy,
            Integer page,
            Integer size,
            Class<T> clazz
    ) {
        Criteria criteria = makeCriteria(startTime, endTime, AdminConstants.MONGO_DB_KEY_LOGDAY);

        if(searchData != null && !searchData.isEmpty()){
            switch (searchType){
                case "GUID":
                    criteria.and("userGuid").is(searchData);
                    break;
                case "NICKNAME":
                    criteria.and("userNickname").is(searchData);
                    break;
                case "ACCOUNT":
                    criteria.and("accountId").is(searchData);
                    break;
                default:
                    log.error("searchType: {} not support", searchType);
                    return null;
            }
        }

        ProjectionOperation projection = Aggregation.project()
                .andExclude("_id")
                .and(AdminConstants.MONGO_DB_KEY_LOGDAY).as(AdminConstants.MONGO_DB_KEY_LOGDAY)
                .and(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID).as(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID)
                .and(AdminConstants.MONGO_DB_KEY_USER_GUID).as("userGuid")
                .and(AdminConstants.MONGO_DB_KEY_USER_NICKNAME).as("userNickname")
                .and(AdminConstants.MONGO_DB_KEY_CREATE_TIME).as(AdminConstants.MONGO_DB_KEY_CREATE_TIME);

        List<AggregationOperation> baseOperations = new ArrayList<>(List.of(
                Aggregation.match(criteria),
                projection,
                Aggregation.sort(orderBy.equals("DESC") ? Sort.Direction.DESC : Sort.Direction.ASC, AdminConstants.MONGO_DB_KEY_LOGDAY)
        ));

        int totalCount = 0;
        if(page != null && page != 0) {
            List<AggregationOperation> countOperations = new ArrayList<>(baseOperations);
            countOperations.add(Aggregation.count().as("total"));

            Aggregation countAggregation = Aggregation.newAggregation(countOperations);
            Document countResult = mongoTemplate.aggregate(
                    countAggregation.withOptions(AggregationOptions.builder().allowDiskUse(true).build()),
                    AdminConstants.MONGO_DB_COLLECTION_CREATE,
                    Document.class
            ).getUniqueMappedResult();
            totalCount = countResult != null ? countResult.getInteger("total") : 0;
        }

        List<AggregationOperation> dataOperations = new ArrayList<>(baseOperations);
        if(page != null && page != 0) {
            int skip = (page - 1) * size;
            dataOperations.add(Aggregation.skip((long) skip));
            dataOperations.add(Aggregation.limit(size));
        }

        Aggregation aggregation = Aggregation.newAggregation(dataOperations);
        List<T> items = mongoTemplate.aggregate(
                aggregation.withOptions(AggregationOptions.builder().allowDiskUse(true).build()),
                AdminConstants.MONGO_DB_COLLECTION_CREATE,
                clazz
        ).getMappedResults();

        return new MongoPageResult<>(items, totalCount);
    }
}
