package Repository.Server;
import Common.Constants;
import Common.Database.DBObject;
import Common.Global;
import Common.Utils.Utils;
import GlobalData.Account.Account;
import GlobalData.Machine.Machine;
import GlobalData.Machine.MachineType;
import GlobalData.RemoteFile.RemoteFile;
import GlobalData.User.User;
import ProjectData.LanguageName;
import Repository.BugReport.BugReport;
import Repository.BugReport.BugReportInterface;
import Repository.BugReportsDatabase;
import Repository.Component.ComponentType;
import Repository.EmailMessage;
import Repository.RepositoryRefuseException;
import Repository.RepositoryServer;
import Repository.Subscribes.Subscriber;
import Visual_DVM_2021.Passes.All.ArchivesBackupPass;
import Visual_DVM_2021.Passes.All.UnzipFolderPass;
import Visual_DVM_2021.Passes.All.ZipFolderPass;
import javafx.util.Pair;
import org.apache.commons.io.FileUtils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
public class ComponentsServer extends RepositoryServer<BugReportsDatabase> {
    public static final String server_dvm_drv = "/home/DVM/dvm_current/dvm_sys/bin/dvm_drv";
    public ComponentsServer() {
        super(BugReportsDatabase.class);
    }
    @Override
    public int getPort() {
        return 7995;
    }
    /*
    @Override
    public boolean needsEmail(String email) {
        if (db.subscribers.containsKey(email)) {
            return db.subscribers.get(email).mailOn == 1;
        }
        return false;
    }
     */
    @Override
    public void DeleteAction(DBObject object) throws Exception {
        if (object instanceof BugReport) {
            BugReport bugReport = (BugReport) object;
            if (!bugReport.project_version.isEmpty())
                Utils.delete_with_check(BugReportInterface.getArchiveFile(bugReport));
        }
    }
    @Override
    public void StartAction() throws Exception {
        if (Global.properties.EmailAdminsOnStart) {
            Vector<String> targets = new Vector<>(Arrays.asList(Global.admins_mails));
            EmailMessage message = new EmailMessage(
                    "Сервер Sapfor запущен",
                    new Date().toString(),
                    targets
            );
            Email(message);
            System.out.println("EMAIL ON START DONE");
        }
    }
    @Override
    protected void Session() throws Exception {
        BugReport bugReport = null;
        Subscriber subscriber = null;
        Account account = null;
        if (!Global.properties.OldServer) {
            switch (code) {
                //<editor-fold desc="Регистрация">
                case CheckSubscriberRole:
                    Print("Проверить роль пользователя");
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    account = (Account) request.object;
                    if (db.subscribers.containsKey(account.email)) {
                        subscriber = db.subscribers.get(account.email);
                    } else {
                        subscriber = new Subscriber();
                        subscriber.name = account.name;
                        subscriber.address = account.email;
                        subscriber.mailOn = 1;
                        db.Insert(subscriber);
                    }
                    response.object = subscriber; //возвращаем информацию о подписчике.
                    break;
                //</editor-fold>
                //<editor-fold desc="БАГ РЕПОРТЫ">
                case ReceiveAllArchives:
                    Print("Отправить клиенту архив всех архивов баг репортов");
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    ZipFolderPass zip = new ZipFolderPass();
                    File archives = new File(Utils.getDateName("Bugs"));
                    if (zip.Do("Bugs", archives.getAbsolutePath())) {
                        response.object = Utils.packFile(archives);
                        Print("Архив успешно запакован");
                    } else throw new RepositoryRefuseException("Не удалось запаковать архивы");
                    break;
                case UpdateBugReport:
                    //-
                    BugReport oldBugReport = (BugReport) request.object;
                    Print("Обновить  баг репорт " + oldBugReport.id);
                    if (db.bugReports.containsKey(oldBugReport.id)) {
                        bugReport = db.bugReports.get(oldBugReport.id);
                        bugReport.SynchronizeFields(oldBugReport);
                        bugReport.change_date = oldBugReport.change_date;
                        db.Update(bugReport);
                        response = new ServerExchangeUnit_2021(ServerCode.OK);
                    } else
                        throw new RepositoryRefuseException("Баг репорт с ключом " + oldBugReport.id + " не существует.");
                    break;
                case UpdateBugReportField: //нужно для дополнений полей
                    //-
                    BugReport oldBugReport_ = (BugReport) request.object;
                    Print("Обновить поле " + request.arg + " баг репорта " + oldBugReport_.id);
                    if (db.bugReports.containsKey(oldBugReport_.id)) {
                        bugReport = db.bugReports.get(oldBugReport_.id);
                        Object newValue = BugReport.class.getField(request.arg).get(oldBugReport_);
                        BugReport.class.getField(request.arg).set(bugReport, newValue);
                        bugReport.change_date = oldBugReport_.change_date;
                        db.Update(bugReport);
                        response = new ServerExchangeUnit_2021(ServerCode.OK);
                    } else
                        throw new RepositoryRefuseException("Баг репорт с ключом " + oldBugReport_.id + " не существует.");
                    break;
                case ReceiveBugReportsDatabase:
                    Print("Получить базу данных баг репортов");
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = Utils.packFile(db.getFile());
                    break;
                case ReceiveBugReport:
                    Print("Скачать баг репорт по ключу " + request.arg);
                    File bugArchive = Paths.get(Global.Home, "Bugs", request.arg).toFile();
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = Utils.packFile(bugArchive);
                    break;
                case SendBugReport:
                    Print("Отправить баг репорт " + request.arg);
                    File bugArchive1 = Paths.get(Global.Home, "Bugs", request.arg).toFile();
                    Utils.unpackFile((byte[]) request.object, bugArchive1);
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    break;
                //</editor-fold>
                case GetComponentsBackups:
                    Print("Получить список сохраненных версий компонента " + request.arg);
                    File backupsDirectory = Paths.get(Global.Home, "Components", request.arg, "Backups").toFile();
                    //--
                    if (backupsDirectory.exists()) {
                        File[] files = backupsDirectory.listFiles(File::isFile);
                        if (files != null) {
                            response = new ServerExchangeUnit_2021(ServerCode.OK);
                            Vector<RemoteFile> res = new Vector<>();
                            for (File file : files)
                                res.add(new RemoteFile(file.getAbsolutePath(), false)); //тут всегда линух.
                            response.object = res;
                        } else
                            throw new RepositoryRefuseException("Не удалось получить список предыдущих версий");
                    } else {
                        //баги еще не создавались. штатная ситуация.
                        response = new ServerExchangeUnit_2021(ServerCode.OK);
                        response.object = new Vector<>();
                    }
                    break;
                case PublishComponent:
                    String[] packed = request.arg.split("\n");
                    String sComponentType = packed[0];
                    String componentFileName = packed[1];
                    String sComponentVersion = packed[2];
                    String componentChangeRecord = request.arg.substring(
                            sComponentType.length() +
                                    componentFileName.length() +
                                    sComponentVersion.length() +
                                    3
                    );
                    Print("Опубликовать компонент " + sComponentType);
                    File componentFile = Paths.get(Global.Home, "Components", sComponentType, componentFileName).toFile();
                    File versionFile = Paths.get(Global.Home, "Components", sComponentType, "version.txt").toFile();
                    File backupsFolder = Paths.get(Global.Home, "Components", sComponentType, "Backups").toFile();
                    //0 архивация старой версии, если она есть.
                    if (componentFile.exists()) {
                        String versionText = "";
                        if (versionFile.exists())
                            versionText = Utils.ReadAllText(versionFile);
                        //---->>
                        Utils.CheckDirectory(backupsFolder);
                        Utils.keepNewFiles(backupsFolder, Global.properties.ComponentsBackUpsCount);
                        //-->>
                        File backupFile = new File(backupsFolder, sComponentType + "_" + versionText);
                        if (backupFile.exists())
                            Utils.delete_with_check(backupFile);
                        FileUtils.moveFile(componentFile, backupFile);
                    }
                    //1 распаковка компонента
                    Utils.unpackFile((byte[]) request.object, componentFile);
                    //2 запись версии компонента
                    FileUtils.writeStringToFile(versionFile, sComponentVersion);
                    //3 запись в журнал компонента
                    File changesLog = Paths.get(Global.Home, "Components", sComponentType, "changes.txt").toFile();
                    FileWriter writer = new FileWriter(changesLog.getAbsolutePath(), true);
                    BufferedWriter bufferWriter = new BufferedWriter(writer);
                    bufferWriter.write(componentChangeRecord);
                    bufferWriter.close();
                    //-
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    break;
                case UpdateComponentMinimalVersion:
                    String[] packed_ = request.arg.split("\n");
                    String sComponentType_ = packed_[0];
                    String sComponentMinimalVersion = packed_[1];
                    Print("Поднять минимальную версию компонента " + sComponentType_);
                    File minimal_versionFile = Paths.get(Global.Home, "Components", sComponentType_, "minimal_version.txt").toFile();
                    FileUtils.writeStringToFile(minimal_versionFile, sComponentMinimalVersion);
                    //-
                    //3 запись в журнал компонента
                    File changesLog_ = Paths.get(Global.Home, "Components", sComponentType_, "changes.txt").toFile();
                    FileWriter writer_ = new FileWriter(changesLog_.getAbsolutePath(), true);
                    BufferedWriter bufferWriter_ = new BufferedWriter(writer_);
                    bufferWriter_.write("Минимальная версия поднята до " + sComponentMinimalVersion + "\n");
                    bufferWriter_.close();
                    //-
                    //-
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    break;
                case ReceiveComponent:
                    String[] packed1 = request.arg.split("\n");
                    //тип/имя файла
                    File componentFile1 = Paths.get(Global.Home, "Components", packed1[0], packed1[1]).toFile();
                    Print("Получить компонент " + packed1[0]);
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = Utils.packFile(componentFile1);
                    break;
                default:
                    throw new RepositoryRefuseException("Неподдерживаемый код: " + code);
                case GetComponentsVersions:
                    Print("Получить актуальные версии компонентов (NEW)");
                    String[] types = request.arg.split("\n");
                    LinkedHashMap<ComponentType, String> response_actual_versions_ = new LinkedHashMap<>();
                    for (String sType : types) {
                        ComponentType componentType = ComponentType.valueOf(sType);
                        File vFile = Paths.get(Global.Home, "Components", sType, "version.txt").toFile();
                        String v_string = Utils.remove(
                                Utils.ReadAllText(vFile),
                                "\n", "\r"
                        );
                        System.out.println(vFile.getAbsolutePath());
                        System.out.println(Utils.DQuotes(v_string));
                        response_actual_versions_.put(componentType, v_string);
                    }
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = response_actual_versions_;
                    break;
                case GetComponentsMinimalVersions:
                    Print("Получить минимальные версии компонентов (NEW)");
                    String[] types_ = request.arg.split("\n");
                    LinkedHashMap<ComponentType, String> response_minimal_versions_ = new LinkedHashMap<>();
                    for (String sType : types_) {
                        ComponentType componentType = ComponentType.valueOf(sType);
                        File vFile = Paths.get(Global.Home, "Components", sType, "minimal_version.txt").toFile();
                        String mv_string = Utils.remove(
                                Utils.ReadAllText(vFile),
                                "\n", "\r"
                        );
                        System.out.println(vFile.getAbsolutePath());
                        System.out.println(Utils.DQuotes(mv_string));
                        response_minimal_versions_.put(componentType, mv_string);
                    }
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = response_minimal_versions_;
                    break;
                case GetComponentChangesLog:
                    Print("Получить журнал изменений компонента " + request.arg);
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = Utils.packFile(Paths.get(Global.Home, "Components", request.arg, "changes.txt").toFile());
                    break;
                case CheckURLRegistered:
                    Print("Проверить учетную запись на машине");
                    String[] data = request.arg.split("\n");
                    String email = data[0];
                    String machineURL = data[1];
                    String login = data[2];
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    response.object = db.workspaces.findWorkspace(email, machineURL, login);
                    break;
                case DVMConvertProject:
                    Print("Сконвертировать проект в DVM код");
                    response = new ServerExchangeUnit_2021(ServerCode.OK);
                    String[] args = request.arg.split("\n");
                    //-
                    String projectName = args[0];
                    LanguageName projectLanguage = LanguageName.valueOf(args[1]);
                    String options = args[2];
                    Vector<String> filesNames = new Vector<>(Arrays.asList(args).subList(3, args.length));
                    //-
                    File workspace = Utils.getTempFileName("convertation");
                    FileUtils.forceMkdir(workspace);
                    File archive = new File(workspace, projectName + ".zip");
                    request.Unpack(archive);
                    File project = new File(workspace, projectName);
                    Vector<String> badFiles = new Vector<>();
                    if (unzip.Do(archive.getAbsolutePath(), workspace.getAbsolutePath()) && project.exists()) {
                        String output = "";
                        for (String fileName : filesNames) {
                            File program = Paths.get(project.getAbsolutePath(), fileName).toFile();
                            System.out.println(program.getAbsolutePath());
                            //--
                            File convertedProgram = Paths.get(program.getParent(),
                                    Utils.getFileNameWithoutExtension(program) + ".DVMH." +
                                            (projectLanguage.equals(LanguageName.fortran) ? "f" : "c")
                            ).toFile();
                            String command =
                                    Utils.DQuotes(server_dvm_drv) + " " +
                                            projectLanguage.getDVMCompile() + "dv " +
                                            options + " "
                                            + Utils.DQuotes(program.getName());
                            System.out.println(command);
                            //--
                            File fileWorkspace = program.getParentFile();
                            Process process = Utils.startScript(workspace, fileWorkspace, Utils.getDateName("convert_script"), command);
                            process.waitFor();
                            String convertationOut = Utils.readAllOutput(process);
                            convertationOut = convertationOut.replace(program.getName(), fileName); //для учета пути.
                            if (!convertationOut.isEmpty())
                                output += convertationOut + "\n";
                            try {
                                if (convertedProgram.exists()) {
                                    FileUtils.forceDelete(program);
                                    convertedProgram.renameTo(program);
                                } else badFiles.add(program.getName());
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                            //--
                        }
                        response.arg = String.join("\n", badFiles) + "|" + output;
                        File resultArchive = new File(workspace, projectName + "_result.zip");
                        if (ComponentsServer.zip.Do(project.getAbsolutePath(), resultArchive.getAbsolutePath())) {
                            response.object = Utils.packFile(resultArchive);
                        } else
                            throw new RepositoryRefuseException("Внутренняя ошибка. Не удалось запаковать версию");
                        //--
                    } else
                        throw new RepositoryRefuseException("Внутренняя ошибка. Не удалось распаковать проект");
                    // File archive = Paths.get(workspace.getAbsolutePath());
                    break;
            }
        } else
            response = new ServerExchangeUnit_2021(ServerCode.OLD);
    }
    @Override
    protected void startAdditionalThreads() {
        backUp.start();
    }
    public static ZipFolderPass zip = new ZipFolderPass();
    public static UnzipFolderPass unzip = new UnzipFolderPass();
    public static ArchivesBackupPass backupSession = new ArchivesBackupPass();
    //-
    public static Vector<Pair<Machine, User>> storages =
            new Vector<>(Arrays.asList(
                    new Pair<>(
                            new Machine("titan", "dvmh.keldysh.ru", 22, MachineType.Server),
                            new User("dvmuser1", "mprit_2011"))
                    /*
                    new Pair<>(
                            new Machine("k100", "k100.kiam.ru", 22, MachineType.Server),
                            new User("dvmuser1", "mprit_2011"))
                     */
            )
            );
    //-
    protected Thread backUp = new Thread(() -> {
        while (true) {
            try {
                //-------------------------------------
                Calendar rightNow = Calendar.getInstance();
                int year = rightNow.get(Calendar.YEAR);
                int month = rightNow.get(Calendar.MONTH);
                int day = rightNow.get(Calendar.DAY_OF_MONTH);
                int hour = rightNow.get(Calendar.HOUR_OF_DAY);
                int minute = rightNow.get(Calendar.MINUTE);
                if ((hour == Global.properties.BackupHour) && (minute == Global.properties.BackupMinute)) {
                    //определить имя папки с багом.
                    String backUpName = year + "_" + (month + 1) + "_" + (day);
                    File todayBackUp = Paths.get(Global.DataBackUpsDirectory.getAbsolutePath(), backUpName).toFile();
                    File todayBackUpArchive = Paths.get(Global.DataBackUpsDirectory.getAbsolutePath(), backUpName + ".zip").toFile();
                    //-
                    File bugsDBBackUp = Paths.get(todayBackUp.getAbsolutePath(), db.getFile().getName()).toFile();
                    File bugsArchives = Paths.get(todayBackUp.getAbsolutePath(), "Bugs.zip").toFile();
                    //-
                    // Чистка старых бекапов на самом сервере.
                    System.out.println(todayBackUp.getAbsolutePath());
                    Utils.keepNewFiles(todayBackUp.getParentFile(), 2);
                    if (!todayBackUpArchive.exists()) {
                        FileUtils.forceMkdir(todayBackUp);
                        Files.copy(db.getFile().toPath(), bugsDBBackUp.toPath());
                        //-
                        zip.Do("Bugs", bugsArchives.getAbsolutePath());
                        zip.Do(todayBackUp.getAbsolutePath(), todayBackUpArchive.getAbsolutePath());
                        Utils.forceDeleteWithCheck(todayBackUp);
                        //-
                        for (Pair<Machine, User> cred : storages) {
                            backupSession.Do(cred.getKey(), cred.getValue(),
                                    todayBackUpArchive
                            );
                        }
                        //bonus backup
                        if (rightNow.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
                            Vector<String> targets = new Vector<>();
                            targets.add(Constants.MailAddress);
                            targets.addAll(Arrays.asList(Global.admins_mails));
                            EmailMessage message = new EmailMessage(
                                    "db backup",
                                    "копия баз данных журнала ошибок",
                                    targets
                            );
                            Email(message, db.getFile());
                        }
                    }
                }
                //-------------------------------------
                Thread.sleep(60000);
            } catch (Exception ex) {
                Global.Log.PrintException(ex);
            }
        }
    });
}
