package ru.mail.teamcity.ssh.config;

import jetbrains.buildServer.serverSide.ServerPaths;
import jetbrains.buildServer.serverSide.crypt.EncryptUtil;
import jetbrains.buildServer.users.SUser;
import org.apache.commons.io.FilenameUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.mail.teamcity.ssh.AppConfiguration;
import ru.mail.teamcity.ssh.ApplicationContextProvider;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Author: g.chernyshev
 * Date: 02.06.15
 */
public class BasicBeanManager {

    private static BasicBeanManager instance = null;

    @NotNull
    private final String CFG_EXT = "xml";

    @NotNull
    private final ConcurrentMap<String, Object> fileLock = new ConcurrentHashMap<String, Object>();

    @NotNull
    private static ServerPaths serverPaths;


    private BasicBeanManager() {
    }

    public static BasicBeanManager getInstance() {
        if (instance == null) {
            synchronized (BasicBeanManager.class) {
                if (instance == null) {
                    instance = new BasicBeanManager();
                    serverPaths = new ApplicationContextProvider().getApplicationContext().getBean("serverPaths", ServerPaths.class);
                }
            }
        }
        return instance;
    }

    @Nullable
    private AbstractBean read(@NotNull final File file) throws JAXBException {
        synchronized (getOrCreateLock(file)) {
            JAXBContext context = JAXBContext.newInstance(AbstractBean.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            if (file.isFile()) {
                AbstractBean bean = (AbstractBean) unmarshaller.unmarshal(file);
                if (EncryptUtil.isScrambled(bean.getPassword())) {
                    bean.setPassword(EncryptUtil.unscramble(bean.getPassword()));
                }
                if (EncryptUtil.isScrambled(bean.getPrivateKey())) {
                    bean.setPrivateKey(EncryptUtil.unscramble(bean.getPrivateKey()));
                }
                return bean;
            }
            return null;
        }
    }

    private void write(@NotNull final File file, @NotNull AbstractBean bean) throws JAXBException {
        synchronized (getOrCreateLock(file)) {
            bean.setPassword(EncryptUtil.scramble(bean.getPassword()));
            bean.setPrivateKey(EncryptUtil.scramble(bean.getPrivateKey()));
            JAXBContext context = JAXBContext.newInstance(AbstractBean.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.marshal(bean, file);
        }
    }

    @NotNull
    private synchronized Object getOrCreateLock(@NotNull final File file) {
        final String fileName = file.getAbsolutePath();
        fileLock.putIfAbsent(fileName, new Object());
        return fileLock.get(fileName);
    }

    @NotNull
    private File getPluginFolder() {
        return new File(serverPaths.getPluginDataDirectory(), AppConfiguration.PLUGIN_NAME);
    }

    @NotNull
    private File getUserFolder(@NotNull SUser user) {
        return new File(getPluginFolder(), user.getUsername());
    }

    @NotNull
    private File getRootFolder(@NotNull SUser user, @NotNull String configFolder) {
        return new File(getUserFolder(user), configFolder);
    }

    @NotNull
    protected File getConfigurationFile(@NotNull SUser user, @NotNull String name, @NotNull String configFolder) {
        if (!FilenameUtils.getExtension(name).equalsIgnoreCase(CFG_EXT)) {
            name += "." + CFG_EXT;
        }
        return new File(getRootFolder(user, configFolder), name);
    }

    @NotNull
    protected List<String> listConfigurationFiles(@NotNull SUser user, @NotNull String configFolder) {
        List<String> files = new ArrayList<String>();

        File root = getRootFolder(user, configFolder);
        if (!root.exists()) {
            return files;
        }
        Collections.addAll(files, root.list(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return FilenameUtils.getExtension(name).equalsIgnoreCase(CFG_EXT);
            }
        }));
        return files;
    }

    @Nullable
    protected <T extends AbstractBean> T load(@NotNull SUser user, @NotNull String name, @NotNull String configFolder, Class<T> type) throws JAXBException {
        File hostConfig = BasicBeanManager.getInstance().getConfigurationFile(user, name, configFolder);
        if (!hostConfig.exists()) {
            return null;
        }
        return type.cast(read(hostConfig));
    }

    protected void save(@NotNull SUser user, @NotNull String configFolder, @NotNull AbstractBean bean) throws JAXBException {
        if (null == bean.getId()) {
            bean.setId(UUID.randomUUID());
        }

        File hostConfig = BasicBeanManager.getInstance().getConfigurationFile(user, bean.getId().toString(), configFolder);
        hostConfig.getParentFile().mkdirs();
        write(hostConfig, bean);
    }

    protected void delete(@NotNull SUser user, @NotNull String configFolder, @NotNull String name) {
        File hostConfig = BasicBeanManager.getInstance().getConfigurationFile(user, name, configFolder);
        if (hostConfig.exists()) {
            hostConfig.delete();
        }
    }
}

@@ -596,83 +596,22 @@
 ava.
-io.FilenameFilter;%0Aimport java.util.ArrayList;%0Aimport java.util.Collections
+util.ArrayList
 ;%0Aim
@@ -4181,19 +4181,13 @@
 ist%3C
-String
 %3E();%0A
+
 %0A   
@@ -4304,24 +4304,25 @@
       %7D%0A
+%0A
         
 Collecti
@@ -4317,122 +4317,124 @@
     
-Collections.addAll(files, root.list(new FilenameFilter() %7B%0A            public boolean accept(File dir, String name
+for (String filename : root.list()) %7B%0A            if (FilenameUtils.getExtension(filename).equalsIgnoreCase(CFG_EXT)
 ) %7B%0A
@@ -4449,22 +4449,29 @@
         
-return
+String name =
  Filenam
@@ -4469,35 +4469,38 @@
 = FilenameUtils.
-get
+remove
 Extension(name).
@@ -4497,40 +4497,308 @@
 ion(
+file
 name)
-.equals
+;%0A%0A                try %7B%0A                    //noinspection ResultOfMethodCall
 Ignore
-Case(CFG_EXT);
+d%0A                    UUID.fromString(name);%0A                    files.add(name);%0A                %7D catch (IllegalArgumentException e) %7B%0A                    // skip file not matched to UUID%0A                %7D
 %0A   
@@ -4817,19 +4817,17 @@
        %7D
-));
+%0A
 %0A       

Load files, matching UUID, also do not include extension to resulted name