package com.caliverse.admin.dynamodb.repository.Impl;

import com.caliverse.admin.domain.entity.BattleEvent;
import com.caliverse.admin.domain.entity.HISTORYTYPE;
import com.caliverse.admin.domain.request.BattleEventRequest;
import com.caliverse.admin.dynamodb.domain.atrrib.BattleEventAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionRegistryAttrib;
import com.caliverse.admin.dynamodb.domain.doc.BattleEventDoc;
import com.caliverse.admin.dynamodb.domain.doc.LandAuctionRegistryDoc;
import com.caliverse.admin.dynamodb.entity.EDayOfWeekType;
import com.caliverse.admin.dynamodb.entity.EOncePeriodRangeType;
import com.caliverse.admin.dynamodb.repository.BaseDynamoDBRepository;
import com.caliverse.admin.dynamodb.repository.BattleEventRepository;
import com.caliverse.admin.dynamodb.service.DynamoDBOperations;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.constants.DynamoDBConstants;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.history.service.DynamodbHistoryLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.enhanced.dynamodb.Key;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;

import static com.caliverse.admin.global.common.utils.CommonUtils.convertUTCDate;
import static com.caliverse.admin.global.common.utils.DateUtils.stringToISODateTime;

@Component
@Slf4j
public class BattleEventRepositoryImpl extends BaseDynamoDBRepository<BattleEventDoc> implements BattleEventRepository {
    public BattleEventRepositoryImpl(DynamoDBOperations operations, DynamodbHistoryLogService dynamodbHistoryLogService, ObjectMapper objectMapper) {
        super(operations, BattleEventDoc.class, dynamodbHistoryLogService, objectMapper);
    }

    @Override
    public int findEventId() {
        List<BattleEventDoc> docs = findByPrefix(
                DynamoDBConstants.PK_KEY_BATTLE_EVENT,
                ""
        );

        if (docs.isEmpty()) return 0;

        BattleEventDoc latestDoc = docs.stream().max(Comparator.comparing(doc -> Integer.parseInt(doc.getSK())))
                .orElse(null);

        try {
            BattleEventAttrib attrib = objectMapper.readValue(latestDoc.getAttribValue(), BattleEventAttrib.class);
            return attrib.getEventId();
        }catch (Exception e){
            log.error(e.getMessage());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        }
    }

    private boolean isEvent(Integer sk){
        Key key = Key.builder()
                .partitionValue(DynamoDBConstants.PK_KEY_BATTLE_EVENT)
                .sortValue(String.valueOf(sk))
                .build();

        BattleEventDoc doc = findById(key);

        return doc != null;
    }

    @Override
    public void insert(BattleEventRequest battleEventRequest) {
        if(isEvent(battleEventRequest.getEventId())){
            log.error("insert EventId: {} is duplication", battleEventRequest.getEventId());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        }

        LocalDateTime nowDate = LocalDateTime.now();
        LocalDateTime start_dt = battleEventRequest.getEventStartDt();
        BattleEvent.BATTLE_REPEAT_TYPE repeatType = battleEventRequest.getRepeatType();

        EOncePeriodRangeType oncePeriodRangeType = getOncePeriodRangeType(repeatType);
        EDayOfWeekType dayOfWeekType = getDayOfWeekType(repeatType);

        HashSet<EDayOfWeekType> hDayOfWeekType = new HashSet<>();
        hDayOfWeekType.add(dayOfWeekType);

        try {
            BattleEventAttrib attrib = new BattleEventAttrib();
            attrib.setAttribType(DynamoDBConstants.ATTRIB_BATTLE_EVENT);
            attrib.setEventId(battleEventRequest.getEventId());
            attrib.setStartDay(stringToISODateTime(start_dt.toLocalDate().atStartOfDay()));
            attrib.setStartHour(start_dt.getHour());
            attrib.setStartMin(start_dt.getMinute());
            attrib.setEndDate(stringToISODateTime(battleEventRequest.getEventEndDt()));
            attrib.setInstanceId(battleEventRequest.getInstanceId());
            attrib.setOncePeriodType(oncePeriodRangeType);
            attrib.setDayOfWeekType(hDayOfWeekType);
            attrib.setConfigDataId(battleEventRequest.getConfigId());
            attrib.setRewardGroupId(battleEventRequest.getRewardGroupId());
            attrib.setHotTime(battleEventRequest.getHotTime());
            attrib.setRoundCount(battleEventRequest.getRoundCount());
            attrib.setActive(true);

            BattleEventDoc doc = new BattleEventDoc();
            doc.setPK(DynamoDBConstants.PK_KEY_BATTLE_EVENT);
            doc.setSK(String.valueOf(battleEventRequest.getEventId()));
            doc.setDocType(DynamoDBConstants.DOC_BATTLE_EVENT);
            doc.setAttribValue(objectMapper.writeValueAsString(attrib));
            doc.setCreatedDateTime(convertUTCDate(nowDate));
            doc.setUpdatedDateTime(convertUTCDate(nowDate));
            doc.setDeletedDateTime(DynamoDBConstants.MIN_DATE);
            doc.setRestoredDateTime(DynamoDBConstants.MIN_DATE);

            save(doc);

            dynamodbHistoryLogService.insertHistoryLog(
                    HISTORYTYPE.BATTLE_EVENT_ADD,
                    HISTORYTYPE.BATTLE_EVENT_ADD.name(),
                    doc
            );
        }catch (Exception e){
            log.error("insert Error: {}", e.getMessage());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        }
    }

    @Override
    public void update(BattleEventRequest battleEventRequest) {
        LocalDateTime nowDate = LocalDateTime.now();
        LocalDateTime start_dt = battleEventRequest.getEventStartDt();

        try {
            Key key = Key.builder()
                    .partitionValue(DynamoDBConstants.PK_KEY_BATTLE_EVENT)
                    .sortValue(String.valueOf(battleEventRequest.getEventId()))
                    .build();

            BattleEventDoc beforeDoc = findById(key);

            if (beforeDoc != null) {
                BattleEventDoc afterDoc = deepCopy(beforeDoc, BattleEventDoc.class);

                BattleEventAttrib attrib = objectMapper.readValue(afterDoc.getAttribValue(), BattleEventAttrib.class);
                attrib.setStartDay(stringToISODateTime(start_dt.toLocalDate().atStartOfDay()));
                attrib.setStartHour(start_dt.getHour());
                attrib.setStartMin(start_dt.getMinute());
                attrib.setEndDate(stringToISODateTime(battleEventRequest.getEventEndDt()));
                attrib.setConfigDataId(battleEventRequest.getConfigId());
                attrib.setRewardGroupId(battleEventRequest.getRewardGroupId());
                attrib.setHotTime(battleEventRequest.getHotTime());
                attrib.setRoundCount(battleEventRequest.getRoundCount());
                attrib.setActive(true);

                afterDoc.setAttribValue(objectMapper.writeValueAsString(attrib));
                afterDoc.setUpdatedDateTime(CommonUtils.convertUTCDate(nowDate));

                update(afterDoc);

                log.info("BattleEventDoc Update Success: {}", objectMapper.writeValueAsString(afterDoc));

                dynamodbHistoryLogService.updateHistoryLog(
                        HISTORYTYPE.BATTLE_EVENT_UPDATE,
                        HISTORYTYPE.BATTLE_EVENT_UPDATE.name(),
                        beforeDoc,
                        afterDoc
                );
            }
        }catch (Exception e){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        }
    }

    @Override
    public void delete(BattleEventRequest battleEventRequest) {
        return;
    }

    @Override
    public void updateStop(BattleEvent battleEvent) {
        try {
            Key key = Key.builder()
                    .partitionValue(DynamoDBConstants.PK_KEY_BATTLE_EVENT)
                    .sortValue(String.valueOf(battleEvent.getEventId()))
                    .build();

            BattleEventDoc beforeDoc = findById(key);

            if (beforeDoc != null) {
                BattleEventDoc afterDoc = deepCopy(beforeDoc, BattleEventDoc.class);

                BattleEventAttrib attrib = objectMapper.readValue(afterDoc.getAttribValue(), BattleEventAttrib.class);
                attrib.setActive(false);

                afterDoc.setAttribValue(objectMapper.writeValueAsString(attrib));
                afterDoc.setUpdatedDateTime(CommonUtils.convertUTCDate(LocalDateTime.now()));

                update(afterDoc);

                log.info("BattleEventDoc Update Stop Success: {}", objectMapper.writeValueAsString(afterDoc));

                dynamodbHistoryLogService.updateHistoryLog(
                        HISTORYTYPE.BATTLE_EVENT_UPDATE,
                        HISTORYTYPE.BATTLE_EVENT_UPDATE.name(),
                        beforeDoc,
                        afterDoc
                );
            }
        }catch (Exception e){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        }
    }

    private EOncePeriodRangeType getOncePeriodRangeType(BattleEvent.BATTLE_REPEAT_TYPE repeatType) {
        return switch (repeatType) {
            case DAY -> EOncePeriodRangeType.Daily;
            case SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY -> EOncePeriodRangeType.Weekly;
            default -> EOncePeriodRangeType.NONE;
        };
    }

    private EDayOfWeekType getDayOfWeekType(BattleEvent.BATTLE_REPEAT_TYPE repeatType) {
        return switch (repeatType) {
            case SUNDAY -> EDayOfWeekType.Sunday;
            case MONDAY -> EDayOfWeekType.Monday;
            case TUESDAY -> EDayOfWeekType.Tuesday;
            case WEDNESDAY -> EDayOfWeekType.Wednesday;
            case THURSDAY -> EDayOfWeekType.Thursday;
            case FRIDAY -> EDayOfWeekType.Friday;
            case SATURDAY -> EDayOfWeekType.Saturday;
            default -> EDayOfWeekType.None;
        };
    }
}
