package io.jenkins.plugins.gitlabbranchsource;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import jenkins.scm.api.SCMFile;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.models.TreeItem;

public class GitLabSCMFile extends SCMFile {

    private final GitLabApi gitLabApi;
    private final String projectPath;
    private final String ref;
    private boolean isDir;

    public GitLabSCMFile(GitLabApi gitLabApi, String projectPath, String ref) {
        super();
        this.gitLabApi = gitLabApi;
        this.isDir = true;
        type(Type.DIRECTORY);
        this.projectPath = projectPath;
        this.ref = ref;
    }

    private GitLabSCMFile(@NonNull GitLabSCMFile parent, String name, boolean isDir) {
        super(parent, name);
        this.gitLabApi = parent.gitLabApi;
        this.projectPath = parent.projectPath;
        this.ref = parent.ref;
        this.isDir = isDir;
    }

    private GitLabSCMFile(GitLabSCMFile parent, String name, Type type) {
        super(parent, name);
        this.gitLabApi = parent.gitLabApi;
        this.projectPath = parent.projectPath;
        this.ref = parent.ref;
        isDir = type == Type.DIRECTORY;
        type(type);
    }

    @NonNull
    @Override
    protected SCMFile newChild(@NonNull String name, boolean assumeIsDirectory) {
        return new GitLabSCMFile(this, name, assumeIsDirectory);
    }

    @NonNull
    @Override
    public Iterable<SCMFile> children() throws IOException, InterruptedException {
        if (!this.isDirectory()) {
            throw new IOException("Cannot get children from a regular file");
        }
        List<TreeItem> treeItems = fetchTree();
        List<SCMFile> result = new ArrayList<>(treeItems.size());
        for (TreeItem c : treeItems) {
            Type t;
            if (c.getType() == TreeItem.Type.TREE) {
                t = Type.DIRECTORY;
            } else if (c.getType() == TreeItem.Type.BLOB) {
                if ("120000".equals(c.getMode())) {
                    // File Mode 120000 is a symlink
                    t = Type.LINK;
                } else {
                    t = Type.REGULAR_FILE;
                }
            } else {
                t = Type.OTHER;
            }
            result.add(new GitLabSCMFile(this, c.getName(), t));
        }
        return result;
    }

    @Override
    public long lastModified() throws IOException, InterruptedException {
        // TODO Fix this
        return 0L;
    }

    @NonNull
    @Override
    protected Type type() throws IOException, InterruptedException {
        if (isDir) {
            return Type.DIRECTORY;
        }
        try {
            gitLabApi.getRepositoryFileApi()
                .getFile(projectPath, getPath(), ref);
            return Type.REGULAR_FILE;
        } catch (GitLabApiException e) {
            if (e.getHttpStatus() != 404) {
                throw new IOException(e);
            }
            try {
                gitLabApi.getRepositoryApi().getTree(projectPath, getPath(), ref);
                return Type.DIRECTORY;
            } catch (GitLabApiException ex) {
                if (e.getHttpStatus() != 404) {
                    throw new IOException(e);
                }
            }
        }
        return Type.NONEXISTENT;
    }

    @NonNull
    @Override
    public InputStream content() throws IOException, InterruptedException {
        if (this.isDirectory()) {
            throw new IOException("Cannot get raw content from a directory");
        } else {
            return fetchFile();
        }
    }

    private InputStream fetchFile() throws IOException {
        try {
            return gitLabApi.getRepositoryFileApi().getRawFile(projectPath, ref, getPath());
        } catch (GitLabApiException e) {
            throw new IOException(String.format("%s not found at %s", getPath(), ref));
        }
    }

    private List<TreeItem> fetchTree() throws IOException {
        try {
            return gitLabApi.getRepositoryApi().getTree(projectPath, getPath(), ref);
        } catch (GitLabApiException e) {
            throw new IOException(String.format("%s not found at %s", getPath(), ref));
        }
    }

}