package com.munjaon.client.server.service;

import com.munjaon.client.server.config.ServerConfig;
import com.munjaon.client.util.LogUtil;
import org.json.simple.JSONObject;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public abstract class Service extends Thread {
    public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]";

    private String LOG_FILE;
    protected LogUtil logger;
    private Long LAST_PROPERTY_LOAD_TIME = 0L;

    /** 서비스 종료여부를 체크하는 변수 */
    boolean bEndProcess = false;

    protected boolean IS_SERVER_RUN;          // 서버가 구동중인지 여부
    protected boolean IS_READY_YN;            // 서비스 구동준비가 완료되었는지 체크
    protected boolean IS_RUN_YN;                // 서비스 실행여부
    protected boolean IS_STOP_YN;               // SERVICE IS TERMINATED >> EXCEPTION, 서비스 Stop여부
    /** SERVICE KILL SYGNAL IS RECEIVED */
    protected boolean IS_KILL_YN = false;

    public Service() {}
    public Service(String serviceName) {
        super(serviceName);
        LOG_FILE = System.getProperty("ROOTPATH") + getProp("LOG_FILE");
    }

    /**
     * config property load
     * @param name
     * @return
     */
    protected String getProp(String name) {
        return getProp(getName(), name);
    }

    /**
     * config property load
     * @param svc
     * @param name
     * @return
     */
    public static String getProp(String svc, String name) {
        return PropertyLoader.getProp(svc, name);
    }

    /**
     * 서비스 실행 여부 config property 조회
     */
    protected void checkRun() {
        this.IS_RUN_YN = getProp("RUN_FLAG") != null && "Y".equals(getProp("RUN_FLAG"));
    }

    /**
     * 클라이언트 실행여부 config property 조회
     */
    protected void checkClientRun() {
        this.IS_SERVER_RUN = getProp("client", "run") != null && "Y".equals(getProp("client", "run"));
    }

    /**
     * 서비스 및 클라이언트 실행여부 조회
     */
    public void reloadCheckRun() {
        if ((System.currentTimeMillis() - this.LAST_PROPERTY_LOAD_TIME) > ServerConfig.INTERVAL_PROPERTY_RELOAD_TIME) {
            checkRun();
            checkClientRun();
            this.LAST_PROPERTY_LOAD_TIME = System.currentTimeMillis();
        }
    }

    /**
     * 서비스 실행 조건 조회
     * @return
     */
    public boolean isRun() {
        return IS_SERVER_RUN && IS_RUN_YN && !IS_STOP_YN && !IS_KILL_YN;
    }

    /**
     * 서비스가 실행준비가 되었는지 조회
     * @return
     */
    public boolean isReady() {
        return IS_READY_YN;
    }

    /**
     * 서비스별 로그파일 초기화
     * @param sLogFile
     */
    protected void setLogFile(String sLogFile) {
        if ( logger != null ) {
            logger.close();
            logger = null;
        }

        logger = new LogUtil( sLogFile );
    }

    /**
     * 실행로그 화면 출력 및 저장
     * @param obj
     */
    protected void saveSystemLog(Object obj) {
        saveLog(obj, true);
    }

    /**
     * 실행로그 저장
     * @param obj
     */
    protected void saveLog(Object obj) {
        saveLog(obj, false);
    }

    /**
     * 실행로그 저장
     * @param obj
     * @param isConsoleOutput
     */
    protected void saveLog(Object obj, boolean isConsoleOutput) {
        if(isConsoleOutput) {
            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{"+ getName() +"}} "+obj);
        }
        if (logger != null) {
            logger.log(obj);
        } else {
            if (obj instanceof Throwable) {
                LogUtil.log(LOG_FILE, obj);
            } else {
                LogUtil.log(LOG_FILE, "{{"+ getName() +"}} "+obj);
            }
        }
    }

    /**
     * 로그파일 초기화
     */
    protected void initLogFile() {
        LOG_FILE = System.getProperty("ROOTPATH") + getProp("LOG_FILE");
        System.out.println("LOG_FILE: " + LOG_FILE);
        setLogFile( LOG_FILE );
        saveSystemLog("Service Log Initializing ... ...");
    }

    public synchronized void Start() {
        super.start();
    }

    protected synchronized void Stop() {
        IS_STOP_YN = true;
        saveSystemLog("Service Stoping...");
    }

    protected synchronized void kill() {
        if( !IS_KILL_YN ) saveSystemLog("Service Killing...");

        IS_KILL_YN = true;
    }

    protected void startService() throws Exception {
        saveLog("startService() called.");
    }

    protected void stopService() throws Exception {
        saveLog("stopService() called.");
    }

    /**
     * 서비스 실행
     */
    @Override
    public void run() {
        while (!IS_KILL_YN) {
            IS_STOP_YN = false;
            try {
                /* 1. 서비스간의 dependency에 따른 체크 */
                checkReady();
                /* 2. 로그 초기화 */
                initLogFile();
                /* 3. Runflag reload */
                reloadCheckRun();
                saveSystemLog("[SERVICE_READY : " + IS_READY_YN + " || SERVICE_RUN : " + IS_RUN_YN);
                if (isRun() && isReady()) {
                    /* 3. 서비스 초기화 */
                    initResources();
                    /* 4. 서비스 시작 */
                    doService();
                    /* 5. 서비스 자원 해제 */
                    releaseResources();
                    saveSystemLog("[SERVICE IS STOPPED] [... ...]");
                } else {
                    saveSystemLog("[SERVICE IS NOT RUNNING] [... ...]");
                }
            } catch (Exception e) {
                IS_STOP_YN = true;
                saveSystemLog(e);
            } finally {
                if(logger != null) { logger.close(); logger = null; }
                /* Exception 발생 대비 자원 해제 */
                releaseResources();
            }

            if(!IS_KILL_YN) {
                try {
                    Thread.sleep(3000);
                } catch(Exception e) {
                }
            }
        }

        try {
            stopService();
        } catch (Exception e) {

        }
        bEndProcess = true;
    }

    public void stopThread() {
        bEndProcess = true;
        kill();

        int i=0;
        while(true) {
            try {
                Thread.sleep(1*1000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }

            if(bEndProcess) {
                break;
            }
        }

        this.interrupt();
        try {
            Thread.sleep(100);
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

    /* 서비스간의 dependency에 따른 체크 */
    public abstract void checkReady();
    /* 추가적인 프로퍼티, 서비스 자원 초기화 */
    public abstract void initResources();
    /* 서비스 자원 해제 */
    public abstract void releaseResources();
    /* 서비스 */
    public abstract void doService();
    /* 모니터링을 위한 메소드 */
    public abstract JSONObject monitorService();
}