package com.tsi.comm.server;

import com.tsi.comm.server.config.ApplicationConfig;
import com.tsi.comm.server.config.TsiCvimServerConfig;
import com.tsi.comm.server.kafka.KafkaConsumerService;
import com.tsi.comm.server.kafka.KafkaProducerService;
import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
import com.tsi.comm.server.process.logging.TsiCvimLoggingProcess;
import com.tsi.comm.server.process.packet.TsiCvimPacketProcess;
import com.tsi.comm.server.repository.ApplicationRepository;
import com.tsi.comm.server.service.TsiCommServerService;
import com.tsi.comm.server.tcp.TsiCvimServer;
import com.tsi.comm.server.vo.TsiAlarmConfigVo;
import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
import com.tsi.comm.server.vo.mariadb.AlarmOccrVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@RequiredArgsConstructor
//@Component
public class ApplicationLifecycle {
    private final ApplicationConfig applicationConfig;
    private final TsiCvimServerConfig serverConfig;
    private final TsiCommServerService tsiCommServerService;
    private final KafkaConsumerService kafkaConsumerService;
    private final KafkaProducerService kafkaProducerService;
    private final TsiCvimPacketProcess packetProcess;
    private final TsiCvimLoggingProcess loggingProcess;
    private final TsiCvimDbmsProcess dbmsProcess;
    private final TsiCvimServer cvimServer;

    private String applicationName;
    private int serverId = 0;

    @PostConstruct
    public void start() {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.info("");
        log.info("************************************************************************************");
        log.info("**                                 TSI CVIM System                                **");
        log.info("**                      CVIM Communication Server Program.                        **");
        log.info("**                                                                   [ver.1.0]    **");
        log.info("** startup: {}", sdfDate.format(new Date()));
        log.info("************************************************************************************");

        this.applicationConfig.setStartSchedule(false);
        serverId = this.serverConfig.getServerId();

        this.applicationName = TsiCommServerApplication.APPLICATION_NAME + "-" + this.serverConfig.getServerId();
        ApplicationRepository.processStateVo.setProcessId(applicationName);

        this.tsiCommServerService.init();
        this.kafkaConsumerService.start();
        this.kafkaProducerService.start();
        this.packetProcess.start();
        this.loggingProcess.start();
        this.dbmsProcess.start();

        // Netty 서버 시작은 백그라운드 스레드에서 실행되므로, 이 메서드를 블로킹하지 않습니다.
        this.cvimServer.run();

        this.serverConfig.setStartup(true);
        try {
            // 시스템 시작 알람 이력 저장
            AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
            alarm.setAlarmCode(TsiAlarmConfigVo.SYS_00);
            alarm.setAlarmTarget(applicationName);
            alarm.setAlarmValue("Running");
            this.tsiCommServerService.insertAlarmOccrHs(alarm);
            this.tsiCommServerService.updateProcessState(0);
        } catch(Exception e) {
            log.error("Error during startup alarm logging: {}", e.getMessage());
        }

        this.applicationConfig.setStartSchedule(true);
        log.info("Application has been started successfully.");
    }

    /**
     * Spring 컨텍스트가 종료될 때, 빈이 소멸되기 직전에 호출됩니다.
     */
    @PreDestroy
    public void stop() {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.error("************************************************************************************");
        log.error("**    Application Terminating: {}", sdfDate.format(new Date()));

        // 종료는 생성의 역순이 안전합니다.

        // 1. 새로운 연결을 막습니다.
        if (this.cvimServer != null) {
            this.cvimServer.stop();
            log.info("Netty Server stopped.");
        }
        if (this.kafkaConsumerService != null) {
            this.kafkaConsumerService.shutdown();
            log.info("Kafka Consumer stopped.");
        }

        // 2. 내부 처리 로직(워커 스레드 풀)을 종료합니다.
        if (this.packetProcess != null) {
            this.packetProcess.stop();
            log.info("Packet Process stopped.");
        }
        if (this.loggingProcess != null) {
            this.loggingProcess.stop();
            log.info("Logging Process stopped.");
        }
        if (this.dbmsProcess != null) {
            this.dbmsProcess.stop();
            log.info("DBMS Process stopped.");
        }

        // 3. 외부로 나가는 통신(Kafka Producer)을 종료합니다.
        if (this.kafkaProducerService != null) {
            this.kafkaProducerService.shutdown();
            log.info("Kafka Producer stopped.");
        }

        // 4. DB에 최종 상태를 기록합니다.
        if (this.tsiCommServerService != null) {
            this.serverConfig.setStartup(false);
            int serverId = this.serverConfig.getServerId();
            this.tsiCommServerService.updateNodeStatusTerm(serverId);
            // ... 시스템 종료 알람 저장 로직 ...
            this.tsiCommServerService.updateProcessState(2);
            log.info("Final DB states updated.");
        }

        log.error("************************************************************************************");
    }
}
