package com.caliverse.admin.dynamodb.repository;

import com.caliverse.admin.domain.entity.EFilterOperator;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.dynamodb.domain.doc.DynamoDBDocBase;
import com.caliverse.admin.dynamodb.dto.PageResult;
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.exception.RestApiException;
import com.caliverse.admin.mongodb.service.DynamodbHistoryLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import software.amazon.awssdk.enhanced.dynamodb.Expression;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
public abstract class BaseDynamoDBRepository<T> implements DynamoDBRepository<T> {
    protected final DynamoDBOperations operations;
    protected final Class<T> entityClass;
    protected final DynamodbHistoryLogService dynamodbHistoryLogService;
    protected final ObjectMapper objectMapper;

    @Override
    public void save(T item) {
        operations.addPutItem(item, entityClass);

        try {
            dynamodbHistoryLogService.insertHistoryLog(
                    LogStatus.SUCCESS,
                    "",
                    (DynamoDBDocBase) item
            );

        } catch (Exception e) {
            dynamodbHistoryLogService.insertHistoryLog(
                    LogStatus.FAILURE,
                    e.getMessage(),
                    (DynamoDBDocBase) item
            );
            throw e;
        }
    }

    @Override
    public void saveWithCondition(T item, Expression condition) {
        operations.addConditionPutItem(item, entityClass, condition);
    }

    @Override
    public void update(T item) {
        DynamoDBDocBase doc = (DynamoDBDocBase) item;
        Key key = Key.builder()
                .partitionValue(doc.getPK())
                .sortValue(doc.getSK())
                .build();
        T beforeDoc = findById(key);

        operations.addUpdateItem(item, entityClass);

        try {
            dynamodbHistoryLogService.updateHistoryLog(
                    LogStatus.SUCCESS,
                    "",
                    (DynamoDBDocBase) beforeDoc,
                    (DynamoDBDocBase) item
            );

        } catch (Exception e) {
            dynamodbHistoryLogService.updateHistoryLog(
                    LogStatus.FAILURE,
                    e.getMessage(),
                    null,
                    (DynamoDBDocBase) item
            );
            throw e;
        }
    }

    @Override
    public void updateWithCondition(T item, Expression condition) {
        operations.addConditionUpdateItem(item, entityClass, condition);
    }

    @Override
    public void delete(Key key) {
        T beforeDoc = findById(key);

        operations.addDeleteItem(key, entityClass);

        try {

            dynamodbHistoryLogService.deleteHistoryLog(
                    LogStatus.SUCCESS,
                    "",
                    (DynamoDBDocBase) beforeDoc
            );

        } catch (Exception e) {
            dynamodbHistoryLogService.deleteHistoryLog(
                    LogStatus.FAILURE,
                    e.getMessage(),
                    (DynamoDBDocBase) beforeDoc
            );
            throw e;
        }
    }

    @Override
    public void deleteWithCondition(Key key, Expression condition) {
        operations.addConditionDeleteItem(key, entityClass, condition);
    }

    @Override
    public T findById(Key key) {
        return operations.getItem(key, entityClass);
    }

    @Override
    public List<T> findAll(Key key) {
        return operations.getQueryItems(key, entityClass);
    }

    @Override
    public List<T> findAllScan(String prefix) {
        return operations.getItemsScanBegins(prefix, entityClass);
    }

    @Override
    public List<T> findAllScan(String prefix, String sortKey) {
        return operations.getItemsScanBegins(prefix, sortKey, entityClass);
    }

    protected <T> T deepCopy(T source, Class<T> valueType) {
        try {
            return objectMapper.readValue(objectMapper.writeValueAsString(source), valueType);
        } catch (JsonProcessingException e) {
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.DYNAMODB_JSON_PARSE_ERROR.getMessage());
        }
    }

    @Override
    public List<T> findByPrefix(String partitionKey, String sortKeyPrefix) {
        if(sortKeyPrefix.isEmpty()){
            return operations.getItemsByPrefix(partitionKey, entityClass);
        }
        else{
            return operations.getItemsByPrefix(partitionKey, sortKeyPrefix, entityClass);
        }
    }

    @Override
    public PageResult<T> findByPaging(
            String partitionKey,
            String sortKeyPrefix,
            Map<String, AttributeValue> exclusiveStartKey
    ) {

        return operations.queryItemsPaging(
                partitionKey,
                sortKeyPrefix,
                "",
                EFilterOperator.NONE,
                "",
                exclusiveStartKey,
                false,
                entityClass
        );
    }

    @Override
    public PageResult<T> findByPaging(
            String partitionKey,
            String sortKeyPrefix,
            String filterAttributeName,
            EFilterOperator filterOperator,
            String filterAttributeValue,
            Map<String, AttributeValue> exclusiveStartKey,
            boolean scanIndexForward
    ) {

        return operations.queryItemsPaging(
                partitionKey,
                sortKeyPrefix,
                filterAttributeName,
                filterOperator,
                filterAttributeValue,
                exclusiveStartKey,
                scanIndexForward,
                entityClass
        );
    }
}
