package io.xpipe.app.cred;

import io.xpipe.app.ext.ValidationException;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.OsFileSystem;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.secret.SecretNoneStrategy;
import io.xpipe.app.secret.SecretRetrievalStrategy;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.KeyValue;
import io.xpipe.app.util.OsType;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface SshIdentityStrategy {

    static List<Class<?>> getAvailable() {
        var l = new ArrayList<Class<?>>();
        l.add(NoIdentityStrategy.class);
        l.add(InPlaceKeyStrategy.class);
        l.add(KeyFileStrategy.class);
        l.add(OpenSshAgentStrategy.class);
        l.add(PasswordManagerAgentStrategy.class);
        l.add(CertificateKeyFileStrategy.class);
        l.add(SecurityKeyStrategy.class);
        if (OsType.ofLocal() != OsType.WINDOWS) {
            l.add(CustomAgentStrategy.class);
        }
        if (GpgAgentStrategy.isSupported()) {
            l.add(GpgAgentStrategy.class);
        }
        if (PageantStrategy.isSupported()) {
            l.add(PageantStrategy.class);
        }
        l.add(OtherExternalIdentityStrategy.class);

        return l;
    }

    static List<Class<?>> getClasses() {
        var l = new ArrayList<Class<?>>();
        l.add(NoIdentityStrategy.class);
        l.add(InPlaceKeyStrategy.class);
        l.add(KeyFileStrategy.class);
        l.add(OpenSshAgentStrategy.class);
        l.add(PasswordManagerAgentStrategy.class);
        l.add(CertificateKeyFileStrategy.class);
        l.add(SecurityKeyStrategy.class);
        l.add(CustomAgentStrategy.class);
        l.add(GpgAgentStrategy.class);
        l.add(PageantStrategy.class);
        l.add(OtherExternalIdentityStrategy.class);

        return l;
    }

    static FilePath getPublicKeyPath(FilePath file) {
        if (file.getExtension().isEmpty() || file.isDotFile()) {
            return FilePath.of(file + ".pub");
        } else {
            return FilePath.of(file.getBaseName() + ".pub");
        }
    }

    static Optional<FilePath> getPublicKeyPath(ShellControl sc, String publicKey) throws Exception {
        if (publicKey == null || publicKey.isBlank()) {
            return Optional.empty();
        }

        var isFile = OsFileSystem.of(sc.getOsType()).isProbableFilePath(publicKey);
        if (isFile && sc.view().fileExists(FilePath.of(publicKey))) {
            return Optional.of(FilePath.of(publicKey));
        }

        try {
            var base = sc.getSystemTemporaryDirectory().join("key.pub");
            var file = sc.view().writeTextFileDeterministic(base, publicKey.strip() + "\n");

            if (sc.getOsType() != OsType.WINDOWS) {
                sc.command(CommandBuilder.of().add("chmod", "400").addFile(file))
                        .executeAndCheck();
            }

            return Optional.of(file);
        } catch (Exception e) {
            ErrorEventFactory.fromThrowable(e).handle();
            return Optional.empty();
        }
    }

    default boolean providesKey() {
        return true;
    }

    default void checkComplete() throws ValidationException {}

    void prepareParent(ShellControl parent) throws Exception;

    void buildCommand(CommandBuilder builder);

    List<KeyValue> configOptions(ShellControl sc) throws Exception;

    default SecretRetrievalStrategy getAskpassStrategy() {
        return new SecretNoneStrategy();
    }

    default boolean supportsIdentityApply() {
        return true;
    }

    PublicKeyStrategy getPublicKeyStrategy();
}
