package com.atlassian.bitbucket.mesh.app;

import com.atlassian.bitbucket.mesh.util.PropertiesUtils;
import com.atlassian.bitbucket.mesh.util.Version;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Properties;

import static java.util.Objects.requireNonNull;

/**
 * Properties that are persisted in the application home directory to track application upgrades and to validate
 * that the home directory layout is compatible with the current application version.
 */
public class HomeProperties {

    private static final String PROP_BUILD_VERSION = "home.build";
    private static final String PROP_LAYOUT_VERSION = "home.layout";

    /**
     * The <i>newest</i> version of the application started using this home directory.
     * <p>
     * When a newer version of the application is started, this version is updated accordingly. However, if the
     * application is downgraded, this version is <i>not</i> updated.
     */
    private final Version build;
    /**
     * The <i>newest</i> version of the home layout applied to this home directory.
     * <p>
     * When a version of the application which uses a newer home layout is started, this version is updated
     * accordingly. However, if the application is downgraded version which uses an older home layout, this
     * version is <i>not</i> updated.
     */
    private final Version layout;

    HomeProperties(@Nonnull Version build, @Nonnull Version layout) {
        this.build = requireNonNull(build, "build");
        this.layout = requireNonNull(layout, "layout");
    }

    private HomeProperties(@Nonnull Properties properties) {
        requireNonNull(properties, "properties");

        build = parseVersion(properties.getProperty(PROP_BUILD_VERSION));
        layout = parseVersion(properties.getProperty(PROP_LAYOUT_VERSION));
    }

    @Nonnull
    public Version getBuild() {
        return build;
    }

    @Nonnull
    public Version getLayout() {
        return layout;
    }

    @Nullable
    static HomeProperties load(@Nonnull Path path) throws IOException {
        return PropertiesUtils.loadIfPresent(path)
                .map(HomeProperties::new)
                .orElse(null);
    }

    void store(@Nonnull Path path) throws IOException {
        requireNonNull(path, "path");

        Properties properties = new Properties();
        properties.setProperty(PROP_BUILD_VERSION, build.toString());
        properties.setProperty(PROP_LAYOUT_VERSION, layout.toString());
        PropertiesUtils.store(properties, path);
    }

    /**
     * Parse the provided value as a version, returning {@code 0.0.0} if no value was provided. {@code 0.0.0} will
     * also be returned if the value provided isn't valid. {@code 0.0.0} is used because it's always "less" than
     * any other version.
     *
     * @param value the value to parse
     * @return the parsed version, which may be {@code 0.0.0} if the value isn't a valid version
     */
    private static Version parseVersion(String value) {
        return StringUtils.isBlank(value) ? new Version(0) : new Version(value);
    }
}
