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.service.TsiCommServerService;
import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
import com.tsi.comm.server.vo.mariadb.AlarmOccrVo;
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.repository.TsiSessionManager;
import com.tsi.comm.server.tcp.TsiCvimServer;
import com.tsi.comm.server.vo.TsiAlarmConfigVo;
import com.tsi.common.spring.SpringUtils;
import com.tsi.common.utils.ApplicationUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.Banner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.ApplicationPidFileWriter;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@SpringBootApplication
@EnableTransactionManagement
@ComponentScan(basePackages = {"com.tsi.common.spring", "com.tsi.comm.server.config", "com.tsi.comm.server"})
public class TsiCommServerApplication implements CommandLineRunner, ApplicationListener<ContextClosedEvent>, InitializingBean, DisposableBean {

    public static final String APPLICATION_NAME = "tsi-comm-server";

    private String applicationName = APPLICATION_NAME;

    public static void main(String[] args) {
        SpringApplication application = new SpringApplicationBuilder()
                .sources(TsiCommServerApplication.class)
                .listeners(new ApplicationPidFileWriter("./conf/" + APPLICATION_NAME + ".pid"))
                .initializers(new AppNameInitializer())
                .build();
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

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

        ApplicationConfig applicationConfig = SpringUtils.getBean(ApplicationConfig.class);
        applicationConfig.setStartSchedule(false);

        TsiCvimServerConfig config = SpringUtils.getBean(TsiCvimServerConfig.class);

        applicationName = APPLICATION_NAME + "-" + config.getServerId();
        ApplicationRepository.processStateVo.setProcessId(applicationName);
//        TsiAlarmManager alarmManager = SpringUtils.getBean(TsiAlarmManager.class);
//        alarmManager.getProcessStateVo().setProcessId(APPLICATION_NAME + "-" + config.getServerId());

        TsiCommServerService tsiCommServerService = SpringUtils.getBean(TsiCommServerService.class);
        tsiCommServerService.init();

        TsiCvimLoggingProcess loggingProcess = SpringUtils.getBean(TsiCvimLoggingProcess.class);
        loggingProcess.start();

        TsiCvimDbmsProcess dbmsProcess = SpringUtils.getBean(TsiCvimDbmsProcess.class);
        dbmsProcess.start();

        TsiCvimPacketProcess packetProcess = SpringUtils.getBean(TsiCvimPacketProcess.class);
        packetProcess.start();

        TsiSessionManager sessionManager = SpringUtils.getBean(TsiSessionManager.class);
        sessionManager.start();

        TsiCvimServer cvimServer = SpringUtils.getBean(TsiCvimServer.class);
        cvimServer.run();

        KafkaConsumerService kafkaConsumerService = SpringUtils.getBean(KafkaConsumerService.class);
        kafkaConsumerService.start();

        KafkaProducerService kafkaProducerService = SpringUtils.getBean(KafkaProducerService.class);
        kafkaProducerService.start();

        config.setStartup(true);
        try {
            // 시스템 시작 알람 이력 저장
            AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
            alarm.setAlarmCode(TsiAlarmConfigVo.SYS_00);
            alarm.setAlarmTarget(applicationName);
            alarm.setAlarmValue("Running");
            tsiCommServerService.insertAlarmOccrHs(alarm);
            tsiCommServerService.updateProcessState(0);
        }
        catch(Exception e) {
            // no logging
        }

        applicationConfig.setStartSchedule(true);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.error("on shutdown hook.");
            applicationConfig.setStartSchedule(false);
            terminate();
        }));
    }

    public void terminate() {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        log.error("************************************************************************************");
        log.error("**    Application Terminated: {}", sdfDate.format(new Date()));
        int serverId = 0;
        try {
            TsiCvimServerConfig config = SpringUtils.getBean(TsiCvimServerConfig.class);
            if (config != null) {
            config.setStartup(false);
            serverId = config.getServerId();
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:config: {}", e.getMessage());
        }

        try {
            TsiSessionManager sessionManager = SpringUtils.getBean(TsiSessionManager.class);
            if (sessionManager != null) {
                sessionManager.stop();
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:sessionManager: {}", e.getMessage());
        }

        try {
            TsiCvimServer cvimServer = SpringUtils.getBean(TsiCvimServer.class);
            if (cvimServer != null) {
                cvimServer.stop();
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:cvimServer: {}", e.getMessage());
        }

        try {
            KafkaProducerService kafkaProducerService = SpringUtils.getBean(KafkaProducerService.class);
            if (kafkaProducerService != null) {
                kafkaProducerService.shutdown();
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:kafkaProducerService: {}", e.getMessage());
        }

        try {
            KafkaConsumerService kafkaConsumerService = SpringUtils.getBean(KafkaConsumerService.class);
            if (kafkaConsumerService != null) {
                kafkaConsumerService.shutdown();
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:kafkaConsumerService: {}", e.getMessage());
        }

        try {
            TsiCommServerService tsiCommServerService = SpringUtils.getBean(TsiCommServerService.class);
            // 노드 통신상태 Off 초기화
            if (tsiCommServerService != null) {
                tsiCommServerService.updateNodeStatusTerm(serverId);
                // 시스템 종료 알람 이력 저장
                AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
                alarm.setAlarmCode(TsiAlarmConfigVo.SYS_00);
                alarm.setAlarmTarget(applicationName);
                alarm.setAlarmValue("Terminated");
                tsiCommServerService.insertAlarmOccrHs(alarm);
                tsiCommServerService.updateProcessState(2);
            }
        } catch (Exception e) {
            log.error("**    Application.terminate:updateProcessState: {}", e.getMessage());
        }
        log.error("************************************************************************************");
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        log.error("{}", contextClosedEvent);
        terminate();
    }

    @Override
    public void destroy() throws Exception {
        log.error("Application destroy.");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    }
}
