/*
 * Copyright (C) 2016-2022 ActionTech.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */

package com.actiontech.dble.services.manager.response;

import com.actiontech.dble.backend.mysql.PacketUtil;
import com.actiontech.dble.btrace.provider.GeneralProvider;
import com.actiontech.dble.config.ErrorCode;
import com.actiontech.dble.config.Fields;
import com.actiontech.dble.config.model.SystemConfig;
import com.actiontech.dble.log.FileUtils;
import com.actiontech.dble.log.general.GeneralLogHelper;
import com.actiontech.dble.log.general.GeneralLogProcessor;
import com.actiontech.dble.net.mysql.*;
import com.actiontech.dble.server.status.GeneralLog;
import com.actiontech.dble.services.manager.ManagerService;
import com.actiontech.dble.services.manager.handler.WriteDynamicBootstrap;
import com.actiontech.dble.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public final class GeneralLogCf {
    private static final Logger LOGGER = LoggerFactory.getLogger(GeneralLogCf.class);
    public static final String FILE_HEADER = "/FAKE_PATH/mysqld, Version: FAKE_VERSION. started with:\n" +
            "Tcp port: 3320  Unix socket: FAKE_SOCK\n" +
            "Time                 Id Command    Argument\n";
    private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();

    private GeneralLogCf() {
    }

    // on/off
    public static class OnOffGeneralLog {
        public OnOffGeneralLog() {
        }

        public static void execute(ManagerService service, boolean isOn) {
            String onOffStatus = isOn ? "enable" : "disable";
            boolean isWrite = false;
            try {
                LOCK.writeLock().lock();
                GeneralProvider.onOffGeneralLog();
                WriteDynamicBootstrap.getInstance().changeValue("enableGeneralLog", isOn ? "1" : "0");
                isWrite = true;
                if (isOn) {
                    GeneralLog.getInstance().setEnableGeneralLog(true);
                    GeneralLogProcessor.getInstance().enable();
                } else {
                    GeneralLog.getInstance().setEnableGeneralLog(false);
                    GeneralLogProcessor.getInstance().disable();
                }
                LOGGER.info(service + " " + onOffStatus + " general_log success by manager");

                OkPacket ok = new OkPacket();
                ok.setPacketId(1);
                ok.setAffectedRows(1);
                ok.setServerStatus(2);
                ok.setMessage((onOffStatus + " general_log success").getBytes());
                ok.write(service.getConnection());
            } catch (Exception e) {
                String msg = onOffStatus + " general_log failed";
                LOGGER.warn(service + " " + msg + " exception：" + e);
                service.writeErrMessage(ErrorCode.ER_YES, msg);
                if (isWrite) {
                    try {
                        WriteDynamicBootstrap.getInstance().changeValue("enableGeneralLog", isOn ? "0" : "1");
                    } catch (Exception ex) {
                        LOGGER.warn("rollback enableGeneralLog failed, exception：{}", ex);
                    }
                }
                return;
            } finally {
                LOCK.writeLock().unlock();
            }
        }
    }

    // show
    public static class ShowGeneralLog {
        public ShowGeneralLog() {
        }

        private static final int FIELD_COUNT = 2;
        private static final ResultSetHeaderPacket HEADER = PacketUtil.getHeader(FIELD_COUNT);
        private static final FieldPacket[] FIELDS = new FieldPacket[FIELD_COUNT];
        private static final EOFPacket EOF = new EOFPacket();

        static {
            int i = 0;
            byte packetId = 0;
            HEADER.setPacketId(++packetId);

            FIELDS[i] = PacketUtil.getField("NAME", Fields.FIELD_TYPE_VAR_STRING);
            FIELDS[i++].setPacketId(++packetId);

            FIELDS[i] = PacketUtil.getField("VALUE", Fields.FIELD_TYPE_VAR_STRING);
            FIELDS[i].setPacketId(++packetId);

            EOF.setPacketId(++packetId);
        }

        public static void execute(ManagerService service) {
            try {
                LOCK.readLock().lock();
                GeneralProvider.showGeneralLog();
                ByteBuffer buffer = service.allocate();
                // write header
                buffer = HEADER.write(buffer, service, true);

                // write fields
                for (FieldPacket field : FIELDS) {
                    buffer = field.write(buffer, service, true);
                }

                // write eof
                buffer = EOF.write(buffer, service, true);

                // write rows
                byte packetId = EOF.getPacketId();

                RowDataPacket row1 = new RowDataPacket(FIELD_COUNT);
                row1.add(StringUtil.encode("general_log", service.getCharset().getResults()));
                row1.add(StringUtil.encode(GeneralLog.getInstance().isEnableGeneralLog() ? "ON" : "OFF", service.getCharset().getResults()));
                row1.setPacketId(++packetId);
                buffer = row1.write(buffer, service, true);

                RowDataPacket row2 = new RowDataPacket(FIELD_COUNT);
                row2.add(StringUtil.encode("general_log_file", service.getCharset().getResults()));
                row2.add(StringUtil.encode(GeneralLog.getInstance().getGeneralLogFile(), service.getCharset().getResults()));
                row2.setPacketId(++packetId);
                buffer = row2.write(buffer, service, true);

                // write last eof
                EOFRowPacket lastEof = new EOFRowPacket();
                lastEof.setPacketId(++packetId);

                lastEof.write(buffer, service);
            } finally {
                LOCK.readLock().unlock();
            }
        }
    }

    // update path
    public static class ReloadGeneralLogFile {
        public ReloadGeneralLogFile() {
        }

        public static void execute(ManagerService service, String filePath) {
            try {
                LOCK.writeLock().lock();
                GeneralProvider.updateGeneralLogFile();
                filePath = StringUtil.removeAllApostrophe(filePath.trim()).trim();
                if (!filePath.startsWith(String.valueOf(File.separatorChar))) {
                    filePath = SystemConfig.getInstance().getHomePath() + File.separatorChar + filePath;
                }
                File newFilePath = new File(filePath);
                filePath = newFilePath.getCanonicalPath();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("start create general log file {}", filePath);
                }
                if (!isSuccessCreateFile(new File(filePath))) {
                    service.writeErrMessage(ErrorCode.ER_YES, "please check the permissions for the file path.");
                    return;
                }
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("end create general log file");
                }
                if (!filePath.equals(GeneralLog.getInstance().getGeneralLogFile())) {
                    WriteDynamicBootstrap.getInstance().changeValue("generalLogFile", filePath);
                    GeneralLog.getInstance().setGeneralLogFile(filePath);
                }
                GeneralLogHelper.putGLog(FILE_HEADER);
                OkPacket ok = new OkPacket();
                ok.setPacketId(1);
                ok.setAffectedRows(1);
                ok.setServerStatus(2);
                ok.setMessage(("reload general log path success").getBytes());
                ok.write(service.getConnection());
            } catch (Exception e) {
                String msg = "reload general log path failed";
                LOGGER.warn(service + " " + msg + " exception：" + e);
                service.writeErrMessage(ErrorCode.ER_YES, msg);
                return;
            } finally {
                LOCK.writeLock().unlock();
            }

        }

        private static boolean isSuccessCreateFile(File file) {
            try {
                if (!file.exists()) {
                    FileUtils.makeParentDirs(file);
                    file.createNewFile();
                }
                if (!file.canWrite()) {
                    return false;
                }
            } catch (IOException e) {
                LOGGER.warn("create general log file failed, exception：{}", e);
                return false;
            }
            return true;
        }
    }

}
