import com.dvelop.d3.server.core.D3Interface
import com.dvelop.d3.server.exceptions.D3Exception
import com.google.gson.Gson
import groovy.json.JsonSlurper
import groovy.transform.Field
import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpDelete
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.util.EntityUtils
import java.nio.charset.StandardCharsets

/*
 * Dieses Skript loescht alle Fristen und Aufgaben fuer einen bestimmten Vertrag aus dem DBS Case Manager Contract.
 *
 * Erstellung: 12.2024 d.velop/mtiel
 * Optimierung: 05.2025
 */

// Start of script
D3Interface d3 = getProperty("d3") as D3Interface
def scriptName = getClass().getName()
d3.log.info("groovyhook - START Script ${scriptName}")


// Konfiguration
def batchSize = 100 // Größe der Batches für SQL-Abfrage und Verarbeitung
def ddfIntContractNo = 11 // DB-Position der Eigenschaft interne Vertragsnummer

def onlyTerms = false // Wenn nur die Fristen geloescht werden sollen, dann true setzen
def onlyTasks = false // Wenn nur die Aufgaben geloescht werden sollen, dann true setzen

def logDirectory = new File("E:\\d3work\\LOGS\\LOG_$scriptName") // Bitte hier den Pfad zum Logverzeichnis eintragen

@Field String baseUri = "https://d3dev.int.hlg.de" // Bitte hier die Basis URL eintragen
@Field String apiKey = "OJRD7qrEhuoIzM5Pt6M59qApakhyNrA/8My81PPx0/ULs+IisvBdZC6GnhcGTzKRwf4ndOtcsQmNby8YPa+D+mfUFsJGjkEsqhLX3h0N8sg=&_z_A0V5ayCRMk4iQs2RWKEDd9HrFriKdSm-G67jqTxNwqW1jZf2OP2l5b7LkBOKnISHL4YtCPCOz81OVhQgE0TQsWylfWnv1"
// Bitte in der oberen Zeile den API Key fuer den User (wie d3ddecsTaskAPIKey) zum Suchen und Loeschen von Aufgaben eintragen
// Ende Konfiguration


def today = new Date()
def formattedDate = today.format("yyyyMMdd")

if (!logDirectory.exists()) {
    logDirectory.mkdir()
}
@Field StringBuilder logBuilder = new StringBuilder()
def logFile = new File(logDirectory.path + "\\${formattedDate}_${System.currentTimeMillis()}_${scriptName}.txt")
logBuilder.append("Info:\t$scriptName - Logfile" + System.getProperty("line.separator"))

def offset = 0 // Offset für die SQL-Abfrage (zur Batch-Verarbeitung)

// Schleife zur Verarbeitung der SQL-Abfrage in Batches
while (true) {
    def internalContractNumbers = d3.sql.executeAndGet("""
WITH Vertragsnummern AS (
    SELECT DISTINCT t.object_id AS intContractNo
    FROM terms_term t
    WHERE NOT EXISTS (
        SELECT 1 FROM firmen_spezifisch f WHERE f.dok_dat_feld_$ddfIntContractNo = t.object_id
    )
    UNION ALL
    SELECT DISTINCT d.internal_contract_number AS intContractNo
    FROM dbsCase_activity d
    WHERE NOT EXISTS (
        SELECT 1 FROM firmen_spezifisch f WHERE f.dok_dat_feld_$ddfIntContractNo = d.internal_contract_number
    )
)
SELECT DISTINCT intContractNo
FROM Vertragsnummern
ORDER BY intContractNo
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY
""", [offset, batchSize])

    if (internalContractNumbers?.isEmpty()) {
        break // Keine weiteren Daten vorhanden, Schleife beenden
    }

    internalContractNumbers?.each {
        def internalContractNo = it.intContractNo

        //Loeschen der Fristen fuer die Vertragsnummer
        if (!onlyTasks) {
            d3.log.info("groovyhook - Es sollen die Fristen fuer den Vertrag ${internalContractNo} geloescht werden!")
            logBuilder.append("Info:\tEs sollen die Fristen fuer den Vertrag ${internalContractNo} geloescht werden!" + System.getProperty("line.separator"))

            def termIds = d3.sql.executeAndGet("SELECT DISTINCT id FROM terms_term WHERE object_id = ?", [internalContractNo])

            if (!termIds || termIds.isEmpty()) {
                d3.log.info("groovyhook - Keine Fristen fuer den Vertrag ${internalContractNo} gefunden.")
                logBuilder.append("Info:\t\tKeine Fristen fuer den Vertrag ${internalContractNo} gefunden." + System.getProperty("line.separator"))
            } else {
                // Tabellen und Abhaengigkeiten
                def termTables = ["terms_idp_ids"      : "Term_id",
                                  "terms_term_metadata": "Term_id",
                                  "terms_term_rem_esc" : "term_id",
                                  "terms_term_callback": "term_id",
                                  "terms_term_links"   : "Term_id",
                                  "terms_term"         : "id",
                                  "terms_interval"     : "id"]

                termIds."id"?.each { id ->
                    termTables.each { table, column ->
                        try {
                            def deleteQuery = "DELETE FROM ${table} WHERE ${column} = ?"
                            if (d3.sql.execute(deleteQuery, [id]) == 1) {
                                d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht.")
                                logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht." + System.getProperty("line.separator"))
                            } else {
                                d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} nicht gefunden.")
                                logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} nicht gefunden." + System.getProperty("line.separator"))
                            }
                        } catch (D3Exception e) {
                            error = true
                            d3.log.error("groovyhook - Fehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}")
                            logBuilder.append("Error:\t\tFehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}" + System.getProperty("line.separator"))

                        }
                    }
                }
            }
            logBuilder.append("\t\t--------------------------------------------------------------------------------" + System.getProperty("line.separator"))
        }


        // Loeschen von Aufgaben fuer die Dokumenten ID
        if (!onlyTerms) {
            logBuilder.append("Info\tEs sollen die Aktivitaeten fuer den Vertrag ${internalContractNo} geloescht werden!" + System.getProperty("line.separator"))

            def activityIds = d3.sql.executeAndGet("SELECT DISTINCT id FROM terms_activity WHERE context_id = ?", [internalContractNo])

            if (!activityIds || activityIds.isEmpty()) {
                d3.log.info("groovyhook - Keine Aktivitaeten fuer den Vertrag ${internalContractNo} gefunden.")
                logBuilder.append("Info:\t\tKeine Aktivitaeten fuer den Vertrag ${internalContractNo} gefunden." + System.getProperty("line.separator"))
            }

            def activityTables = ["terms_activity_detail": "activity_id",
                                  "terms_activity_data"  : "id",
                                  "terms_activity"       : "id"]

            activityIds."id"?.each { id ->
                activityTables.each { table, column ->
                    try {
                        def deleteQuery = "DELETE FROM ${table} WHERE ${column} = ?"
                        if (d3.sql.execute(deleteQuery, [id]) == 1) {
                            d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht.")
                            logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht." + System.getProperty("line.separator"))
                        } else {
                            d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} nicht gefunden.")
                            logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} nicht gefunden." + System.getProperty("line.separator"))
                        }
                    } catch (D3Exception e) {
                        error = true
                        d3.log.error("groovyhook - Fehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}")
                        logBuilder.append("Error:\t\tFehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}" + System.getProperty("line.separator"))

                    }
                }
            }

            logBuilder.append("\t\t--------------------------------------------------------------------------------" + System.getProperty("line.separator"))

            logBuilder.append("Info:\tEs sollen die Aufgaben fuer den Vertrag ${internalContractNo} geloescht werden!" + System.getProperty("line.separator"))

            def caseActivityIds = d3.sql.executeAndGet("SELECT DISTINCT act_id FROM dbsCase_activity WHERE internal_contract_number = ?", [internalContractNo])

            def caseActivityTables = ["dbsCase_activity_recip": "activity_id",
                                      "dbsCase_activity_cond" : "act_id",
                                      "dbsCase_activity"      : "act_id"]

            caseActivityIds."act_id"?.each { id ->
                caseActivityTables.each { table, column ->
                    try {
                        def deleteQuery = "DELETE FROM ${table} WHERE ${column} = ?"
                        if (d3.sql.execute(deleteQuery, [id]) == 1) {
                            d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht.")
                            logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} erfolgreich geloescht." + System.getProperty("line.separator"))
                        } else {
                            if (table == "dbsCase_activity_cond") {
                                d3.log.debug("groovyhook - Es gibt keine Bedingungen für die CMC Aufgabe ${id}.")
                                logBuilder.append("Info:\t\tEs gibt keine Bedingungen für die CMC Aufgabe ${id}." + System.getProperty("line.separator"))
                            } else {
                                d3.log.debug("groovyhook - Eintrag aus Tabelle ${table} mit ID ${id} nicht gefunden.")
                                logBuilder.append("Info:\t\tEintrag aus Tabelle ${table} mit ID ${id} nicht gefunden." + System.getProperty("line.separator"))
                            }
                        }
                    } catch (D3Exception e) {
                        error = true
                        d3.log.error("groovyhook - Fehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}")
                        logBuilder.append("Error:\t\tFehler beim Loeschen der Eintraege aus Tabelle ${table} mit ID ${id}: ${e}" + System.getProperty("line.separator"))

                    }
                }
            }

            def sessionApiKey = getAuthSession(d3)

            //Ermitteln der Task ID
            def uriTaskSearch = "${baseUri}/task/api/tasks/search"

            CloseableHttpClient client = HttpClientBuilder.create().build()

            try {
                HttpPost post = new HttpPost(uriTaskSearch)

                //	JSON Inhalt
                def body = """{
                    "orderBy": "received",
                    "orderDir": "ASC",
                    "filter": {
                        "metadata": {
                            "key": [
                                "internalCaseNumber"
                            ],
                            "values": [
                                 "$internalContractNo"
                            ]
                        }
                    }
                }"""
                StringEntity params = new StringEntity(body, "UTF-8")

                post.addHeader("authorization", "Bearer " + sessionApiKey)
                post.addHeader("content-type", "application/json")
                post.setEntity(params)

                CloseableHttpResponse response = client.execute(post)

                try {
                    if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201) {
                        def js = new JsonSlurper()
                        def parsedJson = js.parseText(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8))
                        def taskIds = parsedJson.tasks?.id?.collect { it.toString() }
                        def taskSubjects = parsedJson.tasks?.subject?.collect { it.toString() }
                        if (!taskIds || taskIds.isEmpty()) {
                            d3.log.info("groovyhook - Es gibt keine Aufgaben zum Vertrag ${internalContractNo}")
                            logBuilder.append("Info:\t\tEs gibt keine Aufgaben zum Vertrag ${internalContractNo}" + System.getProperty("line.separator"))
                        } else {
                            for (def taskId : taskIds) {
                                for (def taskSubject : taskSubjects) {
                                    d3.log.info("groovyhook - Task ID zum Vertrag ${internalContractNo}: ${taskId}")
                                    logBuilder.append("Info:\t\tTask ID zum Vertrag ${internalContractNo}: ${taskId}" + System.getProperty("line.separator"))

                                    //Loeschen der Aufgabe
                                    def uriTaskDelete = "${baseUri}/task/tasks/${taskId}"

                                    def delete = new HttpDelete(uriTaskDelete)
                                    delete.addHeader("authorization", "Bearer " + sessionApiKey)

                                    response = client.execute(delete)

                                    try {
                                        if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201) {
                                            d3.log.info("groovyhook - Task $taskSubject zum Vertrag ${internalContractNo} wurde erfoglreich geloescht")
                                            logBuilder.append("Info:\t\tTask $taskSubject zum Vertrag ${internalContractNo} wurde erfoglreich geloescht" + System.getProperty("line.separator"))
                                        } else if (!(response.getStatusLine().getStatusCode() == 404)) {
                                            d3.log.error("groovyhook - Task $taskId zum Vertrag ${internalContractNo} mit Betreff $taskSubject konnte nicht geloescht werden: HTTP ${response.getStatusLine().getStatusCode()}")
                                            logBuilder.append("Error:\t\tTask $taskId zum Vertrag ${internalContractNo} mit Betreff $taskSubject konnte nicht geloescht werden: HTTP ${response.getStatusLine().getStatusCode()}" + System.getProperty("line.separator"))
                                        }
                                    } catch (D3Exception e) {
                                        d3.log.error("groovyhook - Fehler beim Ausführen von TaskDelete: ${e}")
                                        logBuilder.append("Error:\t\tFehler beim Ausführen von TaskDelete: ${e}" + System.getProperty("line.separator"))
                                    }
                                    finally {
                                        response.close()
                                    }
                                }
                            }
                        }
                    } else {
                        d3.log.error("groovyhook - Task ID zum Vertrag ${internalContractNo} konnte nicht ermittelt werden: HTTP ${response.getStatusLine().getStatusCode()}")
                        logBuilder.append("Error:\t\tTask ID zum Vertrag ${internalContractNo} konnte nicht ermittelt werden: HTTP ${response.getStatusLine().getStatusCode()}" + System.getProperty("line.separator"))
                    }
                } finally {
                    response.close()
                }
            } catch (D3Exception e) {
                d3.log.error("groovyhook - Fehler beim Ausführen von TaskSearch: ${e}")
                logBuilder.append("Error:\t\tFehler beim Ausführen von TaskSearch: ${e}" + System.getProperty("line.separator"))
            }
            logBuilder.append("\t\t--------------------------------------------------------------------------------" + System.getProperty("line.separator"))
        }
    }

    offset += batchSize // Offset für den nächsten Batch erhöhen
}

d3.log.info("groovyhook - END Script ${scriptName}")
logBuilder.append("Info:\tEND Script ${scriptName}" + System.getProperty("line.separator"))

logFile.write(logBuilder.toString())

def getAuthSession(D3Interface d3) {

    def lAuthSession = null
    def lSessionExpire = null

    HashMap<String, String> returnMap = doLogin(d3)

    if (returnMap != null) {
        lAuthSession = returnMap.get("AuthSessionId")
        lSessionExpire = returnMap.get("Expire")
    }

    if (lAuthSession == null || lAuthSession == "") {
        d3.log.error("groovyhook - Keine AuthSession erhalten!")
        logBuilder.append("Error:\t\tKeine AuthSession erhalten!" + System.getProperty("line.separator"))

    }

    return lAuthSession
}

def doLogin(D3Interface d3) {
    HashMap<String, String> returnMap = null
    def loginStatusCode = 0

    CloseableHttpClient client = HttpClientBuilder.create().build()
    try {
        HttpGet loginCall = new HttpGet(baseUri + "/identityprovider/login")
        loginCall.addHeader("authorization", "Bearer " + apiKey)
        loginCall.addHeader("origin", baseUri)
        loginCall.addHeader("accept", "application/json")
        loginCall.addHeader("charset", "utf-8")

        CloseableHttpResponse loginResponse = client.execute(loginCall)
        try {
            loginStatusCode = loginResponse.getStatusLine().getStatusCode()

            if (loginStatusCode == 200 || loginStatusCode == 201) {
                HttpEntity entity = loginResponse.getEntity()
                String json = EntityUtils.toString(entity, StandardCharsets.UTF_8)
                Gson gson = new Gson()
                returnMap = gson.fromJson(json, HashMap.class)
            } else {
                d3.log.error("Error ${loginStatusCode} waehrend des Logins!")
                logBuilder.append("Error:\t\tError ${loginStatusCode} waehrend des Logins!" + System.getProperty("line.separator"))
            }
        } finally {
            loginResponse.close()
        }
    } catch (Exception e) {
        d3.log.error("groovyhook - Fehler waehrend des Logins: ${e}")
        logBuilder.append("Error:\t\tFehler waehrend des Logins: ${e}" + System.getProperty("line.separator"))
    } finally {
        client.close()
    }
    return returnMap
}