package in.cdac.medinfo.csnotk.csnoserv.rest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumProperties;
import in.cdac.medinfo.csnotk.csnolib.disb.common.DISBContants;

//Logs
import in.cdac.medinfo.csnotk.csnolib.util.LoggerUtility;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;



import in.cdac.medinfo.csnotk.csnoserv.CSnoServResource;


@Path("/add")
public class AddController{


    // flag to check if database creation is in progress
    public static boolean createDBFlagCheck = false;
    private static final String DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING = "Database creation process is already running";

    /**
     * Validating entered inputs
     *
     * @param protocol
     * @param hostName
     * @param portNumber
     * @param context
     * @return true if all inputs are verified
     * @throws IOException
     */
    private boolean storeDISBProperties(String protocol, String hostName, String portNumber, String context)
            throws IOException {
        // read system properties file for DISB Configuration
        boolean validateInput = false;
        InputStream input;
        input = new FileInputStream(new File(CSnoServResource.confPath));
        if (input != null) {
            CSnoServResource.prop.load(input);
            input.close();
        }

        if (protocol != null && !protocol.trim().equals("") && protocol.matches("(^[a-zA-Z]+$)*"))
            CSnoServResource.prop.setProperty(DISBContants.PROPERTY_PROTOCOL, protocol);
        else
            throw new IllegalArgumentException("Invalid protocol: " + protocol);

        if (hostName != null && !hostName.trim().equals("")) {
            CSnoServResource.prop.setProperty(DISBContants.PROPERTY_HOSTNAME, hostName);
        } else {
            throw new IllegalArgumentException("Hostname cannot be null or empty");
        }

        if (portNumber != null && !portNumber.trim().equals("") && portNumber.matches("(\\d)*"))
            CSnoServResource.prop.setProperty(DISBContants.PROPERTY_PORT, portNumber);
        else
            throw new IllegalArgumentException("Invalid port number: " + portNumber);

        if (context != null && !context.trim().equals("") && context.matches("(^[a-zA-Z/\\_\\.\\-\\d]+$)*"))
            CSnoServResource.prop.setProperty(DISBContants.PROPERTY_CONTEXT, context);
        else
            throw new IllegalArgumentException("Invalid context: " + context);

        // sets configuration for DISB
        CSnoServResource.initProperties();
        CSnoServResource.initDISBProperties();

        // write or update system properties file for SNOMED CT, ICD-10 Map and Refset
        // DB Configuration
        OutputStream output = new FileOutputStream(CSnoServResource.confPath);

        try {
            if (output != null) {
                CSnoServResource.prop.store(output, "host configuration");
                CSnoServResource.disbAgent.testDISBConnection();
                validateInput = true;
            }
        } finally {
            if (output != null)
                output.close();
        }

        return validateInput;

    }





    // first time configuration
    @POST
    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
    @Produces({ MediaType.TEXT_PLAIN })
    @Path("/config")
    public Response addConfigDetails(@FormParam("data.directory") String dataDir,
                                     @FormParam("log.directory") String logDir, @FormParam("searchReturnlimit") String searchReturnlimit,
                                     @FormParam("suggestReturnlimit") String suggestReturnlimit,
                                     @FormParam("topSuggestReturnlimit") String topSuggestReturnlimit, @FormParam("filepath") String filepath,
                                     @DefaultValue("true") @FormParam("snomedctDB") String snomedctDB,
                                     @FormParam("jdbc_url_icd") String jdbc_url_icd, @FormParam("icd.db.username") String icdDBUsername,
                                     @FormParam("icd.db.password") String icdDBPassword,
                                     @FormParam("icd.jdbc.driver.class") String icdJDBCDriverClass, @FormParam("icd.jar.name") String icdJarName,
                                     @FormParam("icd.db.tablename") String icdDBTablename,
                                     @FormParam("icd.db.term.column") String icdDBTermColumn,
                                     @FormParam("icd.db.code.column") String icdDBCodeColumn) throws URISyntaxException {

        //Setting up system variable 'error_log_dir' for log4j
        if (System.getProperty("error_log_dir")==null)
            System.setProperty("error_log_dir", logDir);
        Logger logger = LogManager.getLogger(AddController.class);
        if (!createDBFlagCheck) {

            try {
                // read system properties file for SNOMED CT, ICD-10 Map and Refset DB
                // Configuration
                InputStream input = new FileInputStream(new File(CSnoServResource.confPath));

                if (input != null) {
                    CSnoServResource.prop.load(input);
                    input.close();
                }

                // read icd properties file for ICD-10 DB Configuration
                input = new FileInputStream(new File(CSnoServResource.confICDPath));

                if (input != null) {
                    CSnoServResource.propICD.load(input);
                    input.close();
                }

            } catch (IOException e) {
                // Log the Exception
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
                return CORSHeaderSupport.addCORSHeader(Response.Status.INTERNAL_SERVER_ERROR, Response.Status.INTERNAL_SERVER_ERROR.toString());
            } catch (Exception e) {
                // Log the Exception
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
                return CORSHeaderSupport.addCORSHeader(Response.Status.INTERNAL_SERVER_ERROR, Response.Status.INTERNAL_SERVER_ERROR.toString() + " : Database creation failed!");
            }

            // check condition and set values for SNOMED CT, ICD Map, Refset Database

            if (CSnoServResource.prop.containsKey("config.flag") && (CSnoServResource.prop.getProperty("config.flag").equalsIgnoreCase("true"))) {
                CSnoServResource.prop.remove("config.flag");
                CSnoServResource.prop.setProperty("config.flag", "false");
            }

            File file = new File(dataDir);
            if (dataDir != null && !dataDir.trim().equals("")) {
                if (!file.exists()) {
                    dataDir = file.getAbsolutePath();
                }
                CSnoServResource.prop.setProperty("data.directory", dataDir);
            }
            if (logDir != null && !logDir.trim().equals("")) {
                file = new File(logDir);
                if (!file.exists()) {
                    logDir = file.getAbsolutePath();
                }
                CSnoServResource.prop.setProperty("log.directory", logDir);
            }

            if (searchReturnlimit != null && !searchReturnlimit.trim().equals("")
                    && searchReturnlimit.matches("(\\d)*"))
                CSnoServResource.prop.setProperty("searchReturnlimit", searchReturnlimit);
            else {
                if (!CSnoServResource.prop.containsKey("searchReturnlimit"))
                    CSnoServResource.prop.setProperty("searchReturnlimit", EnumProperties.SEARCH_RETURNLIMIT_VAL.getValue());
            }

            if (suggestReturnlimit != null && !suggestReturnlimit.trim().equals("")
                    && suggestReturnlimit.matches("(\\d)*"))
                CSnoServResource.prop.setProperty("suggestReturnlimit", suggestReturnlimit);
            else {
                if (!CSnoServResource.prop.containsKey("suggestReturnlimit"))
                    CSnoServResource.prop.setProperty("suggestReturnlimit", EnumProperties.SUGGEST_RETURNLIMIT_VAL.getValue());
            }

            if (topSuggestReturnlimit != null && !topSuggestReturnlimit.trim().equals("")
                    && topSuggestReturnlimit.matches("(\\d)*"))
                CSnoServResource.prop.setProperty("topSuggestReturnlimit", topSuggestReturnlimit);
            else {
                if (!CSnoServResource.prop.containsKey("topSuggestReturnlimit"))
                    CSnoServResource.prop.setProperty("topSuggestReturnlimit", EnumProperties.TOP_SUGGEST_RETURNLIMIT_VAL.getValue());
            }

            // check condition and set values for external ICD-10 Database
            if (jdbc_url_icd != null && !jdbc_url_icd.trim().equals(""))
                CSnoServResource.propICD.setProperty("jdbc_url_icd", jdbc_url_icd);
            if (icdDBUsername != null && !icdDBUsername.trim().equals(""))
                CSnoServResource.propICD.setProperty("icd.db.username", icdDBUsername);
            if (icdDBPassword != null && !icdDBPassword.trim().equals(""))
                CSnoServResource.propICD.setProperty("icd.db.password", icdDBPassword);
            if (icdJDBCDriverClass != null && !icdJDBCDriverClass.trim().equals(""))
                CSnoServResource.propICD.setProperty("icd.jdbc.driver.class", icdJDBCDriverClass);
            if (icdJarName != null && !icdJarName.trim().equals(""))
                CSnoServResource.propICD.setProperty("icd.jar.name", icdJarName);
            if ((icdDBTermColumn != null && !icdDBTermColumn.trim().equals(""))
                    && (icdDBTablename != null && !icdDBTablename.trim().equals(""))
                    && (icdDBCodeColumn != null && !icdDBCodeColumn.trim().equals("")))
                CSnoServResource.propICD.setProperty("icd.query",
                        "select " + icdDBTermColumn + " from " + icdDBTablename + " where " + icdDBCodeColumn + " = ?");

            // sets configuration for SNOMED CT
            CSnoServResource.initProperties();

            boolean createDBFlag = false;

            if (Boolean.valueOf(snomedctDB) && filepath != null) {
                createDBFlagCheck = true;
                createDBFlag = CSnoServResource.agent.createSNOMEDdb(filepath, Boolean.valueOf(snomedctDB));

            }

            if (createDBFlag) {
                try {
                    // write or update system properties file for SNOMED CT, ICD-10 Map and Refset
                    // DB Configuration
                    OutputStream output = new FileOutputStream(CSnoServResource.confPath);
                    if (output != null) {
                        CSnoServResource.prop.store(output, "host configuration");
                        output.close();
                    }

                    // write or update icd properties file for ICD-10 DB Configuration
                    output = new FileOutputStream(CSnoServResource.confICDPath);
                    if (output != null) {
                        CSnoServResource.propICD.store(output, "ICD-10 DB configuration");
                        output.close();
                    }
                } catch (Exception e) {
                    // Log the Exception
                    logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
                    logger.info(LoggerUtility.CONSOLE, "Default path {}" , EnumProperties.ERROR_LOG_PATH.getValue());
                    return CORSHeaderSupport.addCORSHeader(Response.Status.INTERNAL_SERVER_ERROR, Response.Status.INTERNAL_SERVER_ERROR.toString() + " : " + LoggerUtility.createErrorMessage(e));

                }

                // reseting flag
                createDBFlagCheck = false;
                logger.info(LoggerUtility.Log, "Database & Index created successfully!");
                CSnoServResource.errorLogPath = "Database & Index created successfully! | LogFile:" + CSnoServResource.errorLogPath;
                return CORSHeaderSupport.addCORSHeader(Response.Status.OK, CSnoServResource.errorLogPath);
            } else {
                logger.error(LoggerUtility.Exception, "Database & Index creation failed!");
                createDBFlagCheck = false;
                CSnoServResource.errorLogPath = "ERROR:Database & Index creation failed...! | LogFile:"
                        + EnumProperties.ERROR_LOG_PATH.getValue();
                return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,CSnoServResource.errorLogPath);
            }

        } else {// if(!createDBFlagCheck)
            logger.error(LoggerUtility.Exception, DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING);
            return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING);
        }

    }

    @POST
    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
    @Produces({ MediaType.TEXT_PLAIN })
    @Path("/dbconfig")
    public Response addDBConfigDetails(@FormParam("snomedctDB") String snomedctDB,
                                       @FormParam("snomedctRefsetDB") String snomedctRefsetDB, @FormParam("icdmapDB") String icdmapDB,
                                       @FormParam("refsetDB") String refsetDB, @FormParam("extensionDB") String extensionDB,
                                       @FormParam("filename") String filename, @FormParam("simplemapDB") String simplemapDB,
                                       @FormParam("snomedrefsetDB") String snomedRefsetDB, @FormParam("disbDB") String disbDB,
                                       @FormParam("protocol") String protocol, @FormParam("hostname") String hostName,
                                       @FormParam("portnumber") String portNumber, @FormParam("context") String context)
            throws URISyntaxException {
        try {
            if (!createDBFlagCheck) {
                createDBFlagCheck = true;
                // loads configuration files
                CSnoServResource.initProperties();
                Logger logger = LogManager.getLogger(AddController.class);
                CSnoServResource.initICDProperties();
                CSnoServResource.initDISBProperties();
                boolean createDBFlag = false;

                if (Boolean.valueOf(snomedctDB))
                    createDBFlag = CSnoServResource.agent.createSNOMEDdb(filename, Boolean.valueOf(snomedctDB));
                else if (Boolean.valueOf(icdmapDB))
                    createDBFlag = CSnoServResource.mapAgent.createICDMapdb(filename, Boolean.valueOf(icdmapDB));
                else if (Boolean.valueOf(refsetDB))
                    createDBFlag = CSnoServResource.agent.createRefsetdb(filename, Boolean.valueOf(refsetDB));
                else if (Boolean.valueOf(extensionDB))
                    createDBFlag = CSnoServResource.agent.createExtensiondb(filename, Boolean.valueOf(extensionDB));
                else if (Boolean.valueOf(simplemapDB))
                    createDBFlag = CSnoServResource.agent.createSimpleMapdb(filename, Boolean.valueOf(simplemapDB));
                else if (Boolean.valueOf(snomedRefsetDB))
                    createDBFlag = CSnoServResource.agent.createSNOMEDRefsetdb(filename, Boolean.valueOf(snomedRefsetDB));
                else if (Boolean.valueOf(disbDB)) {
                    try {
                        createDBFlag = storeDISBProperties(protocol, hostName, portNumber, context);
                    } catch (Exception e) {
                        createDBFlagCheck = false;
                        logger.error(LoggerUtility.DISBException, LoggerUtility.createErrorMessage(e));
                        return CORSHeaderSupport.addCORSHeader(Response.Status.SERVICE_UNAVAILABLE,Response.Status.SERVICE_UNAVAILABLE.toString() + " : " + e.getMessage());

                    }
                }

                if (createDBFlag) {
                    createDBFlagCheck = false;
                    return CORSHeaderSupport.addCORSHeader(Response.Status.CREATED,Response.Status.CREATED.toString());
                } else {
                    createDBFlagCheck = false;
                    if (Boolean.valueOf(disbDB)) {
                        // check for disb
                        return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,Response.Status.BAD_REQUEST.toString());
                    } else {
                        logger.error(LoggerUtility.Exception, "Database creation failed!");
                        CSnoServResource.errorLogPath = "ERROR: Database creation failed! | LogFile" + CSnoServResource.errorLogPath;
                        return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,CSnoServResource.errorLogPath);
                    }
                }
            } else {
                Logger logger = LogManager.getLogger(AddController.class);
                if (Boolean.valueOf(icdmapDB))
                    logger.error(LoggerUtility.MapICDException, DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING);
                else
                    logger.error(LoggerUtility.Exception, DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING);
                return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,DATABASE_CREATION_PROCESS_IS_ALREADY_RUNNING);
            }
        } catch (Exception e) {
            Logger logger = LogManager.getLogger(AddController.class);
            createDBFlagCheck = false;
            logger.error(LoggerUtility.Exception, "Invalid file path!");
            CSnoServResource.errorLogPath = "ERROR: Invalid file path! | LogFile" + CSnoServResource.errorLogPath;
            return CORSHeaderSupport.addCORSHeader(Response.Status.BAD_REQUEST,CSnoServResource.errorLogPath);
        }
    }


}