package ch.threema.app.threemasafe;

import org.slf4j.Logger;

import java.net.MalformedURLException;
import java.net.URL;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import ch.threema.app.managers.ServiceManager;
import ch.threema.base.ThreemaException;
import static ch.threema.base.utils.LoggingKt.getThreemaLogger;
import okhttp3.Credentials;

import static ch.threema.app.threemasafe.ThreemaSafeService.BACKUP_ID_LENGTH;
import static ch.threema.common.JavaCompat.isNullOrEmpty;
import static ch.threema.common.JavaCompat.toHexString;

public class ThreemaSafeServerInfo {
    private static final Logger logger = getThreemaLogger("ThreemaSafeServerInfo");

    private static final String SAFE_URL_PREFIX = "https://";
    private static final String BACKUP_DIRECTORY_NAME = "backups/";

    // TODO(ANDR-2968): Remove the check for the legacy default server name
    private static final String LEGACY_DEFAULT_SERVER_NAME = "safe-%h.threema.ch";

    @Nullable
    private String customServerName;
    @NonNull
    private final String defaultServerName = getDefaultServerNameFromServerAddressProvider();
    @Nullable
    private String serverUsername;
    @Nullable
    private String serverPassword;

    public ThreemaSafeServerInfo() {
    }

    public ThreemaSafeServerInfo(@Nullable String customServerName, @Nullable String serverUsername, @Nullable String serverPassword) {
        this.serverUsername = serverUsername;
        this.serverPassword = serverPassword;
        this.setCustomServerName(customServerName);
    }

    @Nullable
    public String getCustomServerName() {
        return customServerName;
    }

    public void setCustomServerName(@Nullable String customServerName) {
        if (!isNullOrEmpty(customServerName)) {
            this.customServerName = customServerName.trim().replace(SAFE_URL_PREFIX, "");
            if (defaultServerName.equals(this.customServerName) || LEGACY_DEFAULT_SERVER_NAME.equals(this.customServerName)) {
                this.customServerName = null;
                logger.warn("Tried to set default server as custom server: {}", customServerName);
            }
        } else {
            this.customServerName = null;
        }
    }

    public String getServerUsername() {
        return serverUsername;
    }

    void setServerUsername(String serverUsername) {
        this.serverUsername = serverUsername;
    }

    public String getServerPassword() {
        return serverPassword;
    }

    void setServerPassword(String serverPassword) {
        this.serverPassword = serverPassword;
    }

    public boolean isDefaultServer() {
        return isNullOrEmpty(customServerName);
    }

    URL getBackupUrl(@Nullable byte[] backupId) throws ThreemaException {
        if (backupId == null || backupId.length != BACKUP_ID_LENGTH) {
            throw new ThreemaException("Invalid Backup ID");
        }

        URL serverUrl = getServerUrl(backupId, BACKUP_DIRECTORY_NAME + toHexString(backupId));
        if (serverUrl == null) {
            throw new ThreemaException("Invalid Server URL");
        }

        return serverUrl;
    }

    URL getConfigUrl(@Nullable byte[] backupId) throws ThreemaException {
        URL serverUrl = getServerUrl(backupId, "config");
        if (serverUrl == null) {
            throw new ThreemaException("Invalid URL");
        }

        return serverUrl;
    }

    @Nullable
    String getAuthorization() throws ThreemaException {
        String username = serverUsername, password = serverPassword;

        if ((isNullOrEmpty(serverUsername) || isNullOrEmpty(serverPassword)) && !isNullOrEmpty(customServerName)) {
            int atPos = customServerName.indexOf("@");
            if (atPos > 0) {
                String userInfo = customServerName.substring(0, atPos);

                int colonPos = userInfo.indexOf(":");
                if (colonPos > 0 && colonPos < userInfo.length() - 1) {
                    username = userInfo.substring(0, colonPos);
                    password = userInfo.substring(colonPos + 1);
                }
            }
        }

        if (!isNullOrEmpty(username) && !isNullOrEmpty(password)) {
            return Credentials.basic(username, password);
        }

        var serviceManager = ServiceManager.get();
        var authToken = serviceManager != null ? serviceManager.getApiService().getAuthToken() : null;
        if (authToken != null) {
            return "Token " + authToken;
        }
        return null;
    }

    private String getCustomServerNameOrDefault() {
        if (!isNullOrEmpty(customServerName)) {
            return customServerName;
        } else {
            return defaultServerName;
        }
    }

    private URL getServerUrl(@Nullable byte[] backupId, String filePart) {
        try {
            String shardHash = getShardHash(backupId);
            String serverUrl = SAFE_URL_PREFIX + getCustomServerNameOrDefault().replace("{backupIdPrefix8}", shardHash);

            if (!serverUrl.endsWith("/")) {
                serverUrl += "/";
            }
            serverUrl += filePart;
            return new URL(serverUrl);
        } catch (MalformedURLException e) {
            return null;
        }
    }

    private String getShardHash(@Nullable byte[] backupId) {
        if (backupId != null && backupId.length == BACKUP_ID_LENGTH) {
            return toHexString(backupId).substring(0, 2);
        }
        return "xx";
    }

    public String getHostName() {
        try {
            return new URL(SAFE_URL_PREFIX + getCustomServerNameOrDefault()).getHost();
        } catch (MalformedURLException e) {
            logger.error("Exception", e);
        }
        return "";
    }

    @NonNull
    private String getDefaultServerNameFromServerAddressProvider() {
        ServiceManager serviceManager = ServiceManager.get();
        if (serviceManager == null) {
            logger.error("Cannot retrieve default safe server name as the service manager is null");
            return "";
        }
        try {
            return serviceManager
                .getServerAddressProviderService()
                .getServerAddressProvider()
                .getSafeServerUrl()
                .replace(SAFE_URL_PREFIX, "");
        } catch (ThreemaException e) {
            logger.error("Could not get default safe server name", e);
            return "";
        }
    }
}
