package io.xpipe.ext.base.identity;

import io.xpipe.app.core.AppI18n;
import io.xpipe.app.cred.KeyFileStrategy;
import io.xpipe.app.cred.NoIdentityStrategy;
import io.xpipe.app.cred.SshIdentityStrategy;
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
import io.xpipe.app.platform.Validator;
import io.xpipe.app.prefs.VaultAuthentication;
import io.xpipe.app.secret.EncryptedValue;
import io.xpipe.app.secret.SecretNoneStrategy;
import io.xpipe.app.secret.SecretRetrievalStrategy;
import io.xpipe.app.secret.SecretStrategyChoiceConfig;
import io.xpipe.app.storage.*;
import io.xpipe.app.util.*;
import io.xpipe.app.util.FilePath;

import javafx.beans.property.*;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;

public class SyncedIdentityStoreProvider extends IdentityStoreProvider {

    @Override
    public boolean allowCreation() {
        return DataStorage.get().supportsSync();
    }

    @Override
    public UUID getTargetCategory(DataStore store, UUID target) {
        var cat = DataStorage.get().getStoreCategoryIfPresent(target).orElseThrow();
        var inSynced = DataStorage.get().getCategoryParentHierarchy(cat).stream()
                .anyMatch(dataStoreCategory ->
                        dataStoreCategory.getUuid().equals(DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID));
        return inSynced ? target : DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID;
    }

    @Override
    public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
        SyncedIdentityStore st = (SyncedIdentityStore) store.getValue();

        var user = new SimpleStringProperty(st.getUsername().get());
        var pass = new SimpleObjectProperty<>(st.getPassword());
        var identity = new SimpleObjectProperty<>(st.getSshIdentity());
        var perUser = new SimpleBooleanProperty(st.isPerUser());
        perUser.addListener((observable, oldValue, newValue) -> {
            if (!(identity.getValue() instanceof KeyFileStrategy f)
                    || f.getFile() == null
                    || !f.getFile().isInDataDirectory()) {
                return;
            }

            var source = Path.of(f.getFile().toAbsoluteFilePath(null).toString());
            var target = DataStorage.get()
                    .getDataDir()
                    .resolve("keys", f.getFile().toAbsoluteFilePath(null).getFileName());
            DataStorageSyncHandler.getInstance().addDataFile(source, target, newValue);

            var pub = SshIdentityStrategy.getPublicKeyPath(FilePath.of(source)).asLocalPath();
            var pubTarget = DataStorage.get()
                    .getDataDir()
                    .resolve("keys", pub.getFileName().toString());
            if (Files.exists(pub)) {
                DataStorageSyncHandler.getInstance().addDataFile(pub, pubTarget, newValue);
            }
        });

        var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder()
                .allowKeyFileSync(true)
                .perUserKeyFileCheck(() -> perUser.get())
                .build();

        var passwordChoice = OptionsChoiceBuilder.builder()
                .allowNull(false)
                .property(pass)
                .customConfiguration(
                        SecretStrategyChoiceConfig.builder().allowNone(true).build())
                .available(SecretRetrievalStrategy.getClasses())
                .build()
                .build();

        var handler = DataStorageUserHandler.getInstance();
        return new OptionsBuilder()
                .nameAndDescription("username")
                .addString(user)
                .name("passwordAuthentication")
                .description("passwordAuthenticationDescription")
                .sub(passwordChoice, pass)
                .name("keyAuthentication")
                .description("keyAuthenticationDescription")
                .documentationLink(DocumentationLink.SSH_KEYS)
                .sub(IdentityChoiceBuilder.keyAuthChoice(identity, sshIdentityChoiceConfig), identity)
                .check(val -> Validator.create(val, AppI18n.observable("keyNotSynced"), identity, i -> {
                    var wrong = i instanceof KeyFileStrategy f
                            && f.getFile() != null
                            && !f.getFile().isInDataDirectory();
                    return !wrong;
                }))
                .nameAndDescription(
                        handler.getActiveUser() != null
                                ? (handler.getVaultAuthenticationType() == VaultAuthentication.GROUP
                                        ? "identityPerGroup"
                                        : "identityPerUser")
                                : "identityPerUserDisabled")
                .addToggle(perUser)
                .disable(handler.getActiveUser() == null)
                .bind(
                        () -> {
                            return SyncedIdentityStore.builder()
                                    .username(user.get())
                                    .password(
                                            st.getEncryptedPassword() != null
                                                    ? st.getEncryptedPassword().withValue(pass.get())
                                                    : EncryptedValue.VaultKey.of(pass.get()))
                                    .sshIdentity(
                                            st.getEncryptedSshIdentity() != null
                                                    ? st.getEncryptedSshIdentity()
                                                            .withValue(identity.get())
                                                    : EncryptedValue.VaultKey.of(identity.get()))
                                    .password(EncryptedValue.VaultKey.of(pass.get()))
                                    .sshIdentity(EncryptedValue.VaultKey.of(identity.get()))
                                    .perUser(perUser.get())
                                    .build();
                        },
                        store)
                .buildDialog();
    }

    @Override
    public String summaryString(StoreEntryWrapper wrapper) {
        return wrapper.getEntry().isPerUserStore() ? AppI18n.get("userIdentity") : AppI18n.get("globalIdentity");
    }

    @Override
    public DataStore defaultStore(DataStoreCategory category) {
        return SyncedIdentityStore.builder()
                .password(EncryptedValue.VaultKey.of(new SecretNoneStrategy()))
                .sshIdentity(EncryptedValue.VaultKey.of(new NoIdentityStrategy()))
                .perUser(false)
                .build();
    }

    @Override
    public String getId() {
        return "syncedIdentity";
    }

    @Override
    public List<Class<?>> getStoreClasses() {
        return List.of(SyncedIdentityStore.class);
    }
}
