/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// This verifies local git repository's status.

import org.eclipse.jgit.api.*
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.eclipse.jgit.errors.*

import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.SimpleFileVisitor
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes

buildscript {
  repositories {
    mavenCentral()
  }

  dependencies {
    classpath libs.eclipse.jgit.jgit
  }
}

configure(rootProject) {
  task gitStatus() {
    doFirst {
      try {
        def repository = new FileRepositoryBuilder()
                .setWorkTree(rootProject.projectDir)
                .setMustExist(true)
                .build()

        def ref = repository.findRef("HEAD").getObjectId()
        project.ext.gitRev = ref.name()
        project.ext.gitRevShort = ref.abbreviate(8).name()
        project.ext.gitStatus = new Git(repository).status().call()
      } catch (RepositoryNotFoundException | NoWorkTreeException e) {
        project.ext.gitRev = "(not a git checkout)"
        project.ext.gitRevShort = "(not a git checkout)"
        project.ext.gitStatus = null
      } catch (NotSupportedException e) {
        throw new GradleException("jgit does not support git repository version at this location: ${dir}", e)
      }
    }
  }

  // Verify git working copy does not have any unstaged modified files.
  task checkWorkingCopyClean() {
    dependsOn gitStatus
    doFirst {
      def status = rootProject.ext.gitStatus
      if (status == null) {
        if (file("${rootProject.projectDir}/.git").exists()) {
          // Ignore git worktree branches until jgit supports them.
          logger.warn("WARNING: git worktrees are not supported by jgit (won't check dirty files): ${rootProject.projectDir}")
        } else {
          // Ignore the check. This isn't a git checkout.
          logger.warn("WARNING: Directory is not a valid git checkout (won't check dirty files): ${rootProject.projectDir}")
        }
      } else {
        // git ignores any folders which are empty (this includes folders with recursively empty sub-folders).
        def untrackedNonEmptyFolders = status.untrackedFolders.findAll { path ->
          File location = file("${rootProject.projectDir}/${path}")
          boolean hasFiles = false
          Files.walkFileTree(location.toPath(), new SimpleFileVisitor<Path>() {
            @Override
            FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
              hasFiles = true
              // Terminate early.
              return FileVisitResult.TERMINATE
            }
          })
          return hasFiles
        }

        def offenders = [
            // Exclude staged changes. These are fine in precommit.
            // "(added)": status.added,
            // "(changed)": status.changed,
            // "(removed)": status.removed,
            "(conflicting)": status.conflicting,
            "(missing)": status.missing,
            "(modified)": status.modified,
            "(untracked)": status.untracked,
            "(untracked non-empty dir)": untrackedNonEmptyFolders
        ].collectMany { fileStatus, files ->
          files.collect {file -> "  - ${file} ${fileStatus}" }
        }.sort()

        if (offenders ) {
          def propName = "validation.git.failOnModified"
          def shouldFail = Boolean.valueOf(propertyOrDefault(propName, isCIBuild))
          def msg = "Working copy is not a clean git checkout" +
                  (shouldFail ? " (skip with -P${propName}=false)" : "") + ":\n${offenders.join("\n")}"

          if (shouldFail) {
            throw new GradleException(msg)
          } else {
            logger.warn("NOTE: ${msg}")
          }
        }
      }
    }
  }
}
