package com.caliverse.admin.mongodb.service;

import com.caliverse.admin.domain.entity.FieldChange;
import com.caliverse.admin.domain.entity.EInitDataType;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogCategory;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.dynamodb.domain.doc.DynamoDBDocBase;
import com.caliverse.admin.global.common.constants.CommonConstants;
import com.caliverse.admin.global.component.manager.BusinessProcessIdManager;
import com.caliverse.admin.mongodb.domain.*;
import com.caliverse.admin.mongodb.entity.EDBOperationType;
import com.caliverse.admin.mongodb.repository.BusinessLogRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@AllArgsConstructor
@Slf4j
@Service
public class BusinessLogService {
    private final BusinessLogRepository businessLogRepository;
    private final BusinessProcessIdManager processIdManager;

    @Async
    public CompletableFuture<String> saveLog(LogCategory category, LogStatus status,
                                             String message, Object domain, String worker, String workerIp, String processId, LogAction action) {
        try {
            BusinessLog businessLog = BusinessLog.builder()
                    .logTime(LocalDateTime.now())
                    .category(category)
                    .action(action)
                    .procId(processId)
                    .status(status)
                    .worker(worker == null || worker.isEmpty() ? CommonConstants.SYSTEM : worker)
                    .workerIp(workerIp == null || workerIp.isEmpty() ? "" : workerIp)
                    .message(message)
                    .domain(domain)
                    .build();

            BusinessLog saved = businessLogRepository.save(businessLog);
            log.debug("Business log saved: {}", saved.getId());
            return CompletableFuture.completedFuture(saved.getId());
        } catch (Exception e) {
            log.error("Failed to save business log: {}", e.getMessage(), e);
            return CompletableFuture.failedFuture(e);
        }
    }

    @Async
    public CompletableFuture<String> saveLog(LogCategory category, LogStatus status, LocalDateTime logTime,
                                             String message, Object domain, String worker, String workerIp, String processId, LogAction action) {
        try {
            BusinessLog businessLog = BusinessLog.builder()
                    .logTime(logTime)
                    .category(category)
                    .action(action)
                    .procId(processId)
                    .status(status)
                    .worker(worker == null || worker.isEmpty() ? CommonConstants.SYSTEM : worker)
                    .workerIp(workerIp == null || workerIp.isEmpty() ? "" : workerIp)
                    .message(message)
                    .domain(domain)
                    .build();

            BusinessLog saved = businessLogRepository.save(businessLog);
            log.debug("Business log saved: {}", saved.getId());
            return CompletableFuture.completedFuture(saved.getId());
        } catch (Exception e) {
            log.error("Failed to save business log: {}", e.getMessage(), e);
            return CompletableFuture.failedFuture(e);
        }
    }

    // DYNAMODB 로그
    public CompletableFuture<String> logDynamodb(String tableName, EDBOperationType operationType, String userId, String userIP,
                            DynamoDBDocBase docData, LogStatus status, List<FieldChange> changes, String message, String tranId) {

        DynamodbDomain domain = DynamodbDomain.builder()
                .tranId(tranId)
                .tableName(tableName)
                .operationType(operationType)
                .changed(changes)
                .data(docData)
                .build();

        return saveLog(
                LogCategory.DYNAMODB,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction()
        );
    }

    // MARIADB 로그
    public CompletableFuture<String> logMariadb(String prodId, LogAction logAction, String tableName, EDBOperationType operationType, String userId, String userIP,
                            Object data, LogStatus status, List<FieldChange> changes, String message, String tranId) {

        MariadbDomain domain = MariadbDomain.builder()
                .tranId(tranId)
                .tableName(tableName)
                .operationType(operationType)
                .changed(changes)
                .data(data)
                .build();

        return saveLog(
                LogCategory.MARIADB,
                status,
                message,
                domain,
                userId,
                userIP,
                prodId,
                logAction
        );
    }

    // MessageQueue 로그
    public CompletableFuture<String> logMessageQueue(LogStatus status, String message, String userId, String userIP,
                                String queueName, String targetServer, Object messageContent) {

        MessageQueueDomain domain = MessageQueueDomain.builder()
                .queueName(queueName)
                .targetServer(targetServer)
                .messageContent(messageContent)
                .build();

        return saveLog(
                LogCategory.MESSAGE_QUEUE,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction()
        );
    }

    // Redis 로그
    public CompletableFuture<String> logRedis(LogStatus status, String message, String userId, String userIP,
                         String key, Object value) {

        RedisDomain domain = RedisDomain.builder()
                .key(key)
                .value(value)
                .build();

        return saveLog(
                LogCategory.REDIS,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction()
        );
    }

    // S3 로그
    public CompletableFuture<String> logS3(LogStatus status, String message, String userId, String userIP,
                      String bucket, String key, Long fileSize, String contentType) {

        S3Domain domain = S3Domain.builder()
                .bucket(bucket)
                .key(key)
                .fileSize(fileSize)
                .contentType(contentType)
                .build();

        return saveLog(
                LogCategory.S3,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction()
        );
    }

    // 스케줄러 로그
    public CompletableFuture<String> logScheduler(LogStatus status, String message,
                             String schedulerName, LocalDateTime startTime, LocalDateTime endTime, Object data) {

        SchedulerDomain domain = SchedulerDomain.builder()
                .schedulerName(schedulerName)
                .startTime(startTime)
                .endTime(endTime)
                .info(data)
                .build();

        return saveLog(
                LogCategory.SCHEDULER,
                status,
                message,
                domain,
                "SYSTEM",
                "",
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction()
        );
    }

    // 배치잡 로그
    public CompletableFuture<String> logBatchJob(LogStatus status, String message, String userId, String userIP,
                             String jobName, LocalDateTime startTime, LocalDateTime endTime, Object data, String query) {

        BatchJobDomain domain = BatchJobDomain.builder()
                .jobName(jobName)
                .duration(startTime != null && endTime != null ?
                        Duration.between(startTime, endTime).toMillis() : null)
                .info(data)
                .query(query)
                .build();

        return saveLog(
                LogCategory.BATCH_JOB,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction());
    }

    public CompletableFuture<String> logDataInit(LogStatus status, String message, String userId, String userIP,
                                                 EInitDataType initDataType, String key, String subKey, Object data) {

        DataInitDomain domain = DataInitDomain.builder()
                .initDataType(initDataType)
                .key(key)
                .data(data)
                .build();

        return saveLog(
                LogCategory.DATA_INIT,
                status,
                message,
                domain,
                userId,
                userIP,
                processIdManager.getCurrentProcessId(),
                processIdManager.getCurrentAction());
    }
}
