package com.yupi.project.aop;

import com.yupi.project.config.properties.MqttLoggerProperties;
import com.yupi.project.model.entity.RobotTask;
import com.yupi.project.service.MqttCommunicationLogService;
import com.yupi.project.service.RobotTaskService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class MqttPublishLogAspect {

    private final MqttCommunicationLogService logService;
    private final MqttLoggerProperties loggerProperties;
    private final RobotTaskService robotTaskService;
    private final MqttClient mainMqttClient;

    @Autowired
    public MqttPublishLogAspect(MqttCommunicationLogService logService,
                                MqttLoggerProperties loggerProperties,
                                RobotTaskService robotTaskService,
                                @Qualifier("mqttClientBean") MqttClient mainMqttClient) {
        this.logService = logService;
        this.loggerProperties = loggerProperties;
        this.robotTaskService = robotTaskService;
        this.mainMqttClient = mainMqttClient;
    }

    @Around("execution(* com.yupi.project.service.impl.MqttServiceImpl.sendCommand(..)) || execution(* com.yupi.project.service.MqttService.sendCommand(..))")
    public Object logMqttSendCommand(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!loggerProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        Object[] args = joinPoint.getArgs();
        String robotIdArg = null;
        String commandTypeArg = null;
        String payloadJsonArg = null;
        Long sessionIdArg = null;
        Long associatedTaskIdArg = null;
        Long taskIdForLog = null;

        if (args.length >= 3) {
            robotIdArg = (String) args[0];
            commandTypeArg = args[1].toString();
            payloadJsonArg = (String) args[2];
            if (args.length >= 4 && args[3] instanceof Long) {
                 sessionIdArg = (Long) args[3];
            }
            if (args.length >= 5 && args[4] instanceof Long) {
                associatedTaskIdArg = (Long) args[4];
            }
        }
        
        String topic = null;
        Integer qos = null;
        Boolean retained = false;
        String messageIdForLog = UUID.randomUUID().toString();

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            logService.asyncLogDownstreamMessage(
                    "UNKNOWN_TOPIC_PRE_SEND_FAILURE",
                    payloadJsonArg,
                    qos,
                    retained,
                    mainMqttClient.getClientId(),
                    messageIdForLog,
                    sessionIdArg,
                    null
            );
            log.error("Exception during sendCommand, logged with placeholder data. RobotId: {}, Command: {}", robotIdArg, commandTypeArg, throwable);
            throw throwable;
        }

        if (associatedTaskIdArg != null) {
            taskIdForLog = associatedTaskIdArg;
        } else if (robotIdArg != null && sessionIdArg !=null) {
            log.warn("associatedTaskIdArg was null in MqttPublishLogAspect for robot {}, session {}. Falling back to DB query for taskId.", robotIdArg, sessionIdArg);
            RobotTask latestTask = robotTaskService.findLatestTaskByRobotIdAndSessionId(robotIdArg,sessionIdArg);
            if(latestTask != null){
                taskIdForLog = latestTask.getId();
            }
        }

        logService.asyncLogDownstreamMessage(
                args.length > 0 ? ("CMD_TO_" + args[0]) : "UNKNOWN_TOPIC_POST_SEND",
                payloadJsonArg,
                1,
                false,
                mainMqttClient.getClientId(),
                messageIdForLog, 
                sessionIdArg,
                taskIdForLog 
        );

        return result;
    }
} 