/*
 *  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
 *
 *    https://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.
 */

import grails.doc.git.FetchTagsTask
import grails.doc.dropdown.CreateReleaseDropDownTask
import grails.doc.gradle.PublishGuideTask

plugins {
    id 'base'
    id 'org.asciidoctor.jvm.convert'
    id 'groovy'
}

compileJava.options.release = javaVersion.toInteger()

version = projectVersion

ext {
    grailsCheckOutDir = project.layout.buildDirectory.dir('grails-core/checkout').get().asFile.absolutePath
    grailsTestsCheckOutDir = project.layout.buildDirectory.dir('grails-functional-tests/checkout').get().asFile.absolutePath
    outputDir = layout.buildDirectory.file('docs').get().asFile.absolutePath
    githubBranch = project.findProperty('githubBranch')
    guidePage = '/docs/guide/single.html'
    indexPage = '/docs/index.html'
}

dependencies {
    implementation platform(project(':grails-bom'))
    implementation 'org.apache.grails:grails-docs-core'
    implementation 'org.apache.groovy:groovy'

    // Used to surface versions for groovydoc
    implementation project(':grails-dependencies-starter-web')
    implementation project(':grails-testing-support-core')
    implementation project(':grails-data-hibernate5')
    implementation 'org.apache.grails:grails-gradle-plugins'
    implementation 'org.testcontainers:testcontainers'
    implementation 'org.springframework:spring-core'
    implementation 'org.springframework.boot:spring-boot'
    implementation 'org.apache.groovy.geb:geb-spock'
}

// this task needs to be here instead of the root since bom resolution only occurs when the java / groovy plugins are applied
def combinedGroovydoc = tasks.register('aggregateGroovydoc', Groovydoc)
apply from: rootProject.layout.projectDirectory.file('gradle/docs-dependencies.gradle')
combinedGroovydoc.configure { Groovydoc gdoc ->
    gdoc.windowTitle = "Grails $projectVersion"
    gdoc.docTitle = "Grails $projectVersion"

    def docProjects = rootProject.subprojects
            .findAll { it.findProperty('includeInApiDocs') }

    def sources = docProjects
            .collectMany { Project it ->
                List possibleSources = [it.sourceSets.main]

                def testFixturesSource = it.sourceSets.findByName('testFixtures')
                if (testFixturesSource) {
                    possibleSources.add(testFixturesSource)
                }

                def astSource = it.sourceSets.findByName('ast')
                if (astSource) {
                    possibleSources.add(astSource)
                }
                possibleSources
            }
            .flatten()

    List<File> baseSourceDirs = sources
            .collect { SourceSet it -> it.allSource.srcDirs }
            .flatten()
            .findAll { File srcDir ->
                if (!(srcDir.name in ['java', 'groovy'])) {
                    return false
                }
                srcDir.exists()
            }

    // Collect source directories from included builds (composite builds)
    // Gradle will not provide the included build projects or source sets, so we inspect the file system
    List<File> includedBuildSourceDirs = gradle.includedBuilds
            .collectMany { includedBuild ->
                def sourceDirs = []
                includedBuild.projectDir.eachDir { File dir ->
                    ['src/main/java', 'src/main/groovy'].each { rel ->
                        File sourceDir = new File(dir, rel)
                        if (sourceDir.isDirectory()) {
                            sourceDirs << sourceDir
                        }
                    }
                }
                sourceDirs
            }

    gdoc.source(project.files((baseSourceDirs + includedBuildSourceDirs).unique()))

    gdoc.classpath = files(sources.collect { SourceSet it -> it.compileClasspath.filter(File.&isDirectory) }.flatten().unique())
    gdoc.destinationDir = project.layout.buildDirectory.dir('combined-api/api').get().asFile

    gdoc.inputs.files(gdoc.source).withPropertyName("groovyDocSrc").withPathSensitivity(PathSensitivity.RELATIVE)
    gdoc.outputs.dir(gdoc.destinationDir)
}

String getVersion(String artifact) {
    String version = configurations.runtimeClasspath
            .resolvedConfiguration
            .resolvedArtifacts
            .find {
                it.moduleVersion.id.name == artifact
            }?.moduleVersion?.id?.version
    if (!version) {
        throw new GradleException("Could not find ${artifact} version.")
    }
    version
}

def generateBomDocumentation = tasks.register('generateBomDocumentation')
generateBomDocumentation.configure { Task it ->
    it.dependsOn(':grails-bom:extractConstraints')

    it.description = 'Generates an AsciiDoc table listing Group, Artifact, and Version for project dependencies.'
    it.group = 'documentation'

    it.inputs.files(project(':grails-bom').layout.projectDirectory.asFileTree)
    it.outputs.file(project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM.adoc'))

    def versionsDir = project.layout.projectDirectory.dir('src/en/ref/Versions')
    it.doFirst {
        versionsDir.asFile.mkdirs()
    }

    def bomDocumentFile = project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM.adoc')
    def grailsBomConstraintFile = project(':grails-bom').layout.buildDirectory.file('grails-bom-constraints.adoc')
    it.doLast {
        def bomDocument = bomDocumentFile.asFile
        bomDocument.withWriter { writer ->
            writer.writeLine '== Grails BOM Dependencies'
            writer.writeLine ''
            writer.writeLine 'This document provides information about the dependencies defined in the Grails BOM - also known as `org.apache.grails:grails-bom`. It includes the artifact coordinates, where in the hierarchy the coordinate was defined, and the maven property that defines the version.'
            writer.writeLine ''
            writer.write(grailsBomConstraintFile.get().asFile.text)
            writer.writeLine ''
        }

        it.logger.lifecycle "BOM Dependency Page generated to: ${bomDocument.absolutePath}"
    }
}

def publishGuideTask = tasks.register('publishGuide', PublishGuideTask)
publishGuideTask.configure { PublishGuideTask publish ->
    publish.dependsOn([generateBomDocumentation])

    // No language setting because we want the English guide to be
    // generated with a 'en' in the path, but the source is in 'en'
    // so that it's easy to track with git.
    publish.targetDir = project.layout.buildDirectory.dir('original-guide')
    publish.sourceRepo = "https://github.com/apache/grails-core/edit/$githubBranch/grails-doc/src/en"
    publish.sourceDir = project.layout.projectDirectory.dir('src/en')
    publish.propertiesFiles = project.files(
            rootProject.layout.projectDirectory.file('gradle.properties')
    )

    publish.properties = project.provider {
        def projectVersionForURL = projectVersion.endsWith('-SNAPSHOT') ? 'SNAPSHOT' : projectVersion
        def springVersion = getVersion('spring-core')
        def springBootVersion = getVersion('spring-boot')
        def spockVersion = getVersion("spock-core")
        def spockVersionForURL = getVersion("spock-core")?.split("-")[0]
        def hibernate5Guide = '../grails-data/hibernate5/manual/'
        def api = '../api/'
        def gspTagsRef = '../ref/Tags%20-%20GSP/'
        def controllersRef = '../ref/Controllers/'
        def commandLineRef = '../ref/Command%20Line/'
        def servletApiRef = '../ref/Servlet%20API/'
        def domainClassesRef = '../ref/Domain%20Classes/'
        def constraintsRef = '../ref/Constraints/'
        def groovyVersion = getVersion('groovy') as String
        [
                'api'                      : api,
                'apiFromRef'               : '../' + api,
                'safe'                     : 'UNSAFE', // Make sure any asciidoc security is disabled
                'jakartaee'                : 'https://jakarta.ee/specifications/platform/10/apidocs/',
                'javase'                   : "https://docs.oracle.com/en/java/javase/${javaVersion}/docs/api/",
                'groovyapi'                : "https://docs.groovy-lang.org/${groovyVersion}/html/gapi/",
                'groovyjdk'                : "https://docs.groovy-lang.org/${groovyVersion}/html/groovy-jdk/",
                'groovydocumentation'      : "https://docs.groovy-lang.org/${groovyVersion}/html/documentation/",
                'groovyVersion'            : groovyVersion,
                'springapi'                : "https://docs.spring.io/spring-framework/docs/${springVersion}/javadoc-api/",
                'springdocs'               : "https://docs.spring.io/spring-framework/docs/${springVersion}/",
                'springreference'          : "https://docs.spring.io/spring-framework/reference/${springVersion}/",
                'githubBranch'             : githubBranch,
                'gormApiDocs'              : '../grails-data/api/',
                'gormWebsite'              : '../grails-data/',
                'hibernate5Guide'          : hibernate5Guide,
                'hibernate5GuideFromRef'    : '../' + hibernate5Guide,
                'mongodb5Guide'            : '../grails-data/mongodb/manual/',
                'rxGORMGuide'              : 'https://gorm.grails.org/7.3.4/rx/manual/', // last version released
                'hibernate5Reference'      : 'https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html',
                'springbootapi'            : "https://docs.spring.io/spring-boot/${springBootVersion}/api/java/",
                'springBootReference'      : "https://docs.spring.io/spring-boot/${springBootVersion}/reference/",
                'springBootHowTo'          : "https://docs.spring.io/spring-boot/${springBootVersion}/how-to/",
                'springBootVersion'        : springBootVersion,
                'springVersion'            : springVersion,
                'gradleVersion'            : gradle.gradleVersion,
                'GrailsVersion'            : projectVersion,
                'grailsVersionForURL'      : projectVersionForURL,
                'version'                  : projectVersion,
                'gormVersion'              : projectVersion,
                'hibernate5Version'        : getVersion("hibernate-core-jakarta"),
                'spockVersion'             : spockVersion,
                'spockDocs'                : "https://spockframework.org/spock/docs/${spockVersionForURL}/",
                'testingSupportVersion'    : getVersion("grails-testing-support-core"),
                'grailsGradlePluginVersion': getVersion("grails-gradle-plugins"),
                'gparsdocs'                : "https://gpars.org/$gparsVersion/groovydoc",
                'rxjavadocs'               : 'https://reactivex.io/RxJava/1.x/javadoc',
                'rxjava2docs'              : 'https://reactivex.io/RxJava/2.x/javadoc',
                'gspTagsRef'               : gspTagsRef,
                'gspTagsRefFromRef'        : '../' + gspTagsRef,
                'cacheTagsRef'             : '../ref/Tags%20-%20Cache/',
                'tagLibrariesRef'          : '../ref/Tag%20Libraries/',
                'guidePath'                : '../../guide/',
                'controllersRef'           : controllersRef,
                'controllersRefFromRef'    : '../' + controllersRef,
                'commandLineRef'           : commandLineRef,
                'commandLineRefFromRef'    : '../' + commandLineRef,
                'domainClassesRef'         : domainClassesRef,
                'domainClassesRefFromRef'  : '../' + domainClassesRef,
                'servletApiRef'            : servletApiRef,
                'servletApiRefFromRef'     : '../' + servletApiRef,
                'constraintsRef'           : constraintsRef,
                'constraintsRefFromRef'    : '../' + constraintsRef,
                'versionsRef'              : '../ref/Versions/',

        ] as Map<String, String>
    }
    publish.propertiesWithFilePaths = project.provider {
        [
                'cacheExampleSourceDir': rootProject.layout.projectDirectory.dir('grails-test-examples/cache').asFile,
                'sourcedir'            : rootProject.layout.projectDirectory.asFile,
                'functionalSourceDir'  : rootProject.layout.projectDirectory.asFile
        ] as Map<String, String>
    }
}

def createReleaseDropdownTask = tasks.register('createReleaseDropdown', CreateReleaseDropDownTask)
createReleaseDropdownTask.configure { CreateReleaseDropDownTask it ->
    it.dependsOn(publishGuideTask)

    it.filesToAddDropdowns = project.layout.buildDirectory.files('modified-guide/guide/single.html', 'modified-guide/index.html')
    it.sourceDocsDirectory = project.layout.buildDirectory.dir('original-guide')
    it.modifiedPagesDirectory = project.layout.buildDirectory.dir('modified-guide')
}

def fetchTagsTask = project.tasks.register('fetchTags', FetchTagsTask)
createReleaseDropdownTask.configure {
    dependsOn(fetchTagsTask)
}

def docsTask = tasks.register('docs', Sync)
docsTask.configure { Sync it ->
    it.dependsOn(combinedGroovydoc, createReleaseDropdownTask, ':grails-data-docs-stage:docs')
    it.group = 'documentation'

    def manualDocsDir = project.layout.buildDirectory.dir('modified-guide')
    def apiDocsDir = project.layout.buildDirectory.dir('combined-api')
    def dataDocsDir = rootProject.layout.projectDirectory.dir('grails-data-docs/stage/build/docs')

    def mergedDocsDir = project.layout.buildDirectory.dir('docs')

    it.inputs.dir manualDocsDir
    it.inputs.dir apiDocsDir
    it.inputs.dir dataDocsDir
    it.inputs.dir project.layout.projectDirectory.dir('src')

    it.outputs.dir mergedDocsDir

    it.from manualDocsDir, apiDocsDir, dataDocsDir
    it.into mergedDocsDir
}

tasks.register('dist', Zip).configure { Zip it ->
    it.dependsOn(docsTask)
    it.from(outputDir)
}

artifacts {
    archives dist
}

// the groovy plugin is applied to this project, but we do not build a jar file since
// the dependencies are only used for resolving versions from the bom
tasks.withType(Jar).configureEach {
    enabled = false
}