package com.utic.center.utic.traf.server;

import com.utic.center.common.spring.SpringUtils;
import com.utic.center.common.utils.ApplicationUtils;
import com.utic.center.utic.traf.server.config.ApplicationConfig;
import com.utic.center.utic.traf.server.dao.repository.utic.UticTrafficRepository;
import com.utic.center.utic.traf.server.dto.ViewTextDto;
import com.utic.center.utic.traf.server.repository.ApplicationRepository;
import com.utic.center.utic.traf.server.service.LinkParamService;
import com.utic.center.utic.traf.server.service.LinkPatternService;
import com.utic.center.utic.traf.server.service.LinkService;
import com.utic.center.utic.traf.server.service.ProcessStateService;
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.utic.center.common.spring", "com.utic.center.utic.traf.server.config", "com.utic.center.utic.traf.server", "com.utic.center.common.aspect"})
public class UticTrafServerApplication implements CommandLineRunner, ApplicationListener<ContextClosedEvent>, InitializingBean, DisposableBean {

    private static boolean isTerminal = false;
    public static final String APPLICATION_NAME = "utic-traf-server";

    public static void main(String[] args) {
        Path pidPath = ApplicationUtils.getApplicationPidPath(UticTrafServerApplication.class, APPLICATION_NAME);
        if (ApplicationUtils.isRunning(pidPath)) {
            log.warn("[WAN] UticTrafServerApplication is already running. Check the PID file: {}", pidPath);
            System.exit(1);
        }

        SpringApplication application = new SpringApplicationBuilder()
                .sources(UticTrafServerApplication.class)
                .listeners(new ApplicationPidFileWriter("./conf/" + APPLICATION_NAME + ".pid"))
                .registerShutdownHook(true)
                .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("**                          UTIC Center TRAF System                               **");
        log.info("**               UTIC Center ITS Traffic Processing 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);

        // TRAFFIC_CENTER VIEW ACTIVE TABLE Checking
        UticTrafficRepository repo = SpringUtils.getBean(UticTrafficRepository.class);
        ViewTextDto activeView = repo.findTrafficCenterViewDdl();
        if (activeView == null) {
            log.error("UticTrafServerApplication.run: loading traffic_center view checking Failed. program shutdown.");
            SpringApplication.exit(SpringUtils.getApplicationContext());
            System.exit(0);
            return;
        }
        ApplicationRepository.setCurrentTrafficCenterView(activeView.getViewText());

        // 프로세스 상태 저장
        ProcessStateService processStateService = SpringUtils.getBean(ProcessStateService.class);
        processStateService.processStart();

        // 링크 기본 정보 조회
        LinkService linkService = SpringUtils.getBean(LinkService.class);
        linkService.processing();

        // 파라미터 정보 조회(가공분석을 위해 미리 수행)
        LinkParamService linkParamService = SpringUtils.getBean(LinkParamService.class);
        linkParamService.processing();

        // 패턴정보 조회
        LinkPatternService linkPatternService = SpringUtils.getBean(LinkPatternService.class);
        linkPatternService.processing();

        applicationConfig.setStartSchedule(true);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.error("on shutdown hook.");
            applicationConfig.setStartSchedule(false);
            terminate();
            try {
                Path pidPath = ApplicationUtils.getApplicationPidPath(UticTrafServerApplication.class, APPLICATION_NAME);
                Files.deleteIfExists(pidPath);
            } catch (IOException ignored) {
            }
        }));
    }

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

            log.error("************************************************************************************");
            log.error("**    Application Terminated: {}", sdfDate.format(new Date()));
            try {
                ApplicationConfig applicationConfig = SpringUtils.getBean(ApplicationConfig.class);
                applicationConfig.setStartSchedule(false);

                ProcessStateService processStateService = SpringUtils.getBean(ProcessStateService.class);
                processStateService.processStop();
            } catch (Exception e) {
                log.error("**    Application Terminated: {}", e.getMessage());
            }
            log.error("************************************************************************************");
        }
    }

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

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

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("Application afterPropertiesSet");
    }
}
