plugins {
    id 'java'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '8.1.1'
}

def appVersionProps = new Properties()
def appVersionFile = file('VERSION.properties')
if (appVersionFile.exists()) {
    appVersionFile.withInputStream { appVersionProps.load(it) }
}
def serverVersionFromFile = String.valueOf(appVersionProps.getProperty('server.version', '1.1_codex')).trim()

allprojects {
    tasks.withType(JavaCompile).configureEach {
        options.encoding = 'UTF-8'
    }
}

group = 'shine'
version = serverVersionFromFile

tasks.jar {
    enabled = false     // это что бы не создавала обычный джар, а будет только толстый джар
}

tasks.processResources {
    filteringCharset = 'UTF-8'
    filesMatching('application.properties') {
        expand(projectVersion: project.version.toString())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.eclipse.jetty:jetty-server:11.0.20'           // WS сервер
    implementation 'org.eclipse.jetty:jetty-servlet:11.0.20'
    implementation 'org.eclipse.jetty.websocket:websocket-jetty-server:11.0.20'

    implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'              // шифрование
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'  // json

//    implementation 'org.slf4j:slf4j-api:2.0.9'
    implementation 'ch.qos.logback:logback-classic:1.5.6'
    // Logback (реализация SLF4J + классический модуль)
    runtimeOnly "ch.qos.logback:logback-classic:1.5.6"

    testImplementation platform('org.junit:junit-bom:5.10.0')
    testImplementation 'org.junit.jupiter:junit-jupiter'

    implementation "org.slf4j:slf4j-api:2.0.16"          // вызов логгера


//    runtimeOnly "org.apache.logging.log4j:log4j-core:2.24.3"            // Реализация: Log4j2 пишет в файл/консоль
//    runtimeOnly "org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3"     // Реализация: Log4j2 пишет в файл/консоль


    implementation project(':shine-server-config')       // модуль настроек из application.properties
    implementation project(":shine-server-log")          // модуль логирования и уведомления админов

    implementation project(':shine-server-crypto')       // модуль сервера для работы с криптографией
    implementation project(':shine-server-db')           // модуль для работы с БД содержит и сущности из БД и саму работу с БД
    implementation project(':shine-server-blockchain')   // модуль для работы с блокчейном

    implementation project(':shine-server-geo')           // модуль для определения геолокации по IP

    implementation project(':shine-server-net-protocol') // Модуль отвечающий за протокол (классы Net..Request/Response
    implementation project(':shine-server-net-server')   // Хэндлеры для обработки сетевых запросов




    // -------------------- ТЕСТЫ (JUnit 5) --------------------
    // Один BOM на всё семейство JUnit (Jupiter + Platform модули)
    testImplementation platform("org.junit:junit-bom:5.10.2")

    // JUnit Jupiter (Test, BeforeAll, Assertions)
    testImplementation "org.junit.jupiter:junit-jupiter"

    // JUnit Platform Suite (@Suite, @SelectClasses)
    testImplementation "org.junit.platform:junit-platform-suite-api"
    testRuntimeOnly  "org.junit.platform:junit-platform-suite-engine"

    // Нужно для компиляции RussianSummaryListener (org.junit.platform.launcher.*)
    // и чтобы JUnit мог подхватить listener при запуске
    testImplementation "org.junit.platform:junit-platform-launcher"
    testRuntimeOnly  "org.junit.platform:junit-platform-launcher"
}

test {
    useJUnitPlatform()
}

application {
    // 👇 класс с методом main
    mainClass = 'server.ws.WsServer'
}


java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}


shadowJar {
    // создаём 1 файл без постфиксов
    archiveBaseName.set('shine-server')
    archiveClassifier.set('')
    archiveVersion.set('')
    mergeServiceFiles()

    manifest {
        attributes(
                'Main-Class': 'server.ws.WsServer'
        )
    }
}


tasks.named('test') {
    enabled = false
}

tasks.register('cleanServerLogs') {
    group = "!!test"
    description = "Clear server logs/app.log and remove rolled log files"

    doLast {
        File logsDir = file('logs')
        if (!logsDir.exists()) {
            logsDir.mkdirs()
        }

        File appLog = new File(logsDir, 'app.log')
        if (!appLog.exists()) {
            appLog.createNewFile()
        }
        appLog.text = ''

        fileTree(logsDir) {
            include 'app.*.log'
        }.files.each { File f ->
            if (!f.delete()) {
                throw new GradleException("Failed to delete log file: ${f.absolutePath}")
            }
        }

        println "Server logs cleared: ${logsDir.absolutePath}"
    }
}

tasks.register('integrationTest', JavaExec) {
    group = "!!test"
    description = "Clean data → kill 7070 → start WS → run all IT tests"

    classpath = sourceSets.test.runtimeClasspath
    mainClass = "test.it.IT_RunAllCleanStartWsMain"

    // пробрасываем системные флаги (по желанию)
    systemProperty "it.debug", System.getProperty("it.debug", "true")
    systemProperty "it.login", System.getProperty("it.login", "anya24")

    dependsOn testClasses
}

tasks.named('build') {
    finalizedBy tasks.named('integrationTest')
}

tasks.register('deployServer', JavaExec) {
    group = "!!deployment"
    description = "Build → upload to server → restart service (без удаления БД, без IT тестов)"

    classpath = sourceSets.test.runtimeClasspath
    mainClass = "test.it.IT_DeployRestartNoCleanNoTestsMain"

    // можно переопределить при запуске:
    // ./gradlew deployServer -Dit.remoteHost=... -Dit.wsUri=...
    dependsOn shadowJar
    systemProperty "it.remoteHost", System.getProperty("it.remoteHost", "194.87.0.247")
    systemProperty "it.remoteUser", System.getProperty("it.remoteUser", "user")
    systemProperty "it.remoteDir",  System.getProperty("it.remoteDir", "/home/user/docker/shine-server")
    systemProperty "it.service", System.getProperty("it.service", "shine-server")
    systemProperty "it.localJar", System.getProperty("it.localJar", "build/libs/shine-server.jar")

    dependsOn testClasses
}

tasks.register('deployServerWithBackupCleanAndTests') {
    group = "!!deployment"
    description = "BLOCKED: удаление БД на проде запрещено, используйте только миграции"

    doLast {
        def msg = """
[BLOCKED] Удаление базы данных на продакшен-сервере отключено.
Причина: в базе уже есть пользовательские сообщения.
Дальше используйте только миграции схемы БД.
Задача остановлена намеренно.
""".stripIndent().trim()
        println msg
        throw new GradleException(msg)
    }
}

tasks.register('deployServerNoCleanNoTests', JavaExec) {
    group = "!!deployment"
    description = "Build → upload to server → restart service (no data clean, no IT tests)"

    classpath = sourceSets.test.runtimeClasspath
    mainClass = "test.it.IT_DeployRestartNoCleanNoTestsMain"

    // можно переопределить при запуске:
    // ./gradlew deployServerNoCleanNoTests -Dit.remoteHost=...
    dependsOn shadowJar
    systemProperty "it.remoteHost", System.getProperty("it.remoteHost", "194.87.0.247")
    systemProperty "it.remoteUser", System.getProperty("it.remoteUser", "user")
    systemProperty "it.remoteDir",  System.getProperty("it.remoteDir", "/home/user/docker/shine-server")
    systemProperty "it.service", System.getProperty("it.service", "shine-server")
    systemProperty "it.localJar", System.getProperty("it.localJar", "build/libs/shine-server.jar")

    dependsOn testClasses
}

def registerWebDeployTask = { String taskName, String target, String descriptionText ->
    tasks.register(taskName, Exec) {
        group = "!!deployment"
        description = descriptionText
        workingDir = rootDir
        commandLine 'bash', file('deploy_shine-PWA.sh').absolutePath, target
    }
}

registerWebDeployTask('deployWEB_Production', 'prod', 'Deploy WEB (production: shineup.me)')
registerWebDeployTask('deployWEB_ui_1', 'ui_1', 'Deploy WEB (ui-1.shineup.me)')
registerWebDeployTask('deployWEB_ui_2', 'ui_2', 'Deploy WEB (ui-2.shineup.me)')
registerWebDeployTask('deployWEB_ui_3', 'ui_3', 'Deploy WEB (ui-3.shineup.me)')
registerWebDeployTask('deployWEB_DrygMira', 'ui_drygmira', 'Deploy WEB (ui-drygmira.shineup.me)')
registerWebDeployTask('deployWEB_Milana', 'ui_milana', 'Deploy WEB (ui-milana.shineup.me)')
registerWebDeployTask('deployWEB_Aidar', 'ui_aidar', 'Deploy WEB (ui-aidar.shineup.me)')

tasks.register('startLocal', Exec) {
    group = "!!run"
    description = "Builds server, starts local WS server and local HTTP UI for end-to-end local testing"

    dependsOn shadowJar
    dependsOn cleanServerLogs

    workingDir = rootDir
    def wsPort = System.getProperty("localWsPort", "7070")
    def webPort = System.getProperty("localWebPort", "8088")

    commandLine 'bash', '-lc', """
      set -euo pipefail
      JAR_PATH="${file('build/libs/shine-server.jar').absolutePath}"
      UI_DIR="${file('shine-UI').absolutePath}"
      WS_PORT="${wsPort}"
      WEB_PORT="${webPort}"

      is_port_busy() {
        local port="\$1"
        if command -v ss >/dev/null 2>&1; then
          ss -ltnH "sport = :\$port" | grep -q .
        elif command -v lsof >/dev/null 2>&1; then
          lsof -iTCP:"\$port" -sTCP:LISTEN >/dev/null 2>&1
        else
          return 1
        fi
      }

      pick_free_port() {
        local p="\$1"
        while is_port_busy "\$p"; do
          p=\$((p + 1))
        done
        echo "\$p"
      }

      WS_PORT="\$(pick_free_port "\$WS_PORT")"
      WEB_PORT="\$(pick_free_port "\$WEB_PORT")"

      echo "Starting SHiNE local stack..."
      echo "WS server port: \$WS_PORT"
      echo "UI HTTP port: \$WEB_PORT"
      echo "Client URL: http://localhost:\$WEB_PORT/?localWsPort=\$WS_PORT"

      java -Dserver.port="\$WS_PORT" -jar "\$JAR_PATH" &
      SERVER_PID=\$!
      trap 'kill \$SERVER_PID 2>/dev/null || true' EXIT INT TERM

      CLIENT_URL="http://localhost:\$WEB_PORT/?localWsPort=\$WS_PORT"
      if command -v xdg-open >/dev/null 2>&1; then
        (xdg-open "\$CLIENT_URL" >/dev/null 2>&1 || true) &
      elif command -v open >/dev/null 2>&1; then
        (open "\$CLIENT_URL" >/dev/null 2>&1 || true) &
      elif command -v cmd.exe >/dev/null 2>&1; then
        (cmd.exe /c start "" "\$CLIENT_URL" >/dev/null 2>&1 || true) &
      else
        echo "Browser auto-open is not available on this host. Open manually: \$CLIENT_URL"
      fi

      if command -v python3 >/dev/null 2>&1; then
        (cd "\$UI_DIR" && python3 -m http.server "\$WEB_PORT")
      else
        (cd "\$UI_DIR" && python -m http.server "\$WEB_PORT")
      fi
    """
}

tasks.register('startLocalWithBuild') {
    group = "!!run"
    description = "Build server (build) and then start local stack (startLocal)"

    dependsOn tasks.named('build')
    dependsOn tasks.named('startLocal')
}

tasks.register('deployPromoSolanaDevnet', Exec) {
    group = "!!deployment"
    description = "Деплой отдельного временного сервиса SHiNE-promo-solana-devnet на сервер через домен shineup.me"

    // Этот сервис не входит в основной multi-module build данного репозитория.
    // Поэтому деплой выполняется через отдельный Gradle-проект в подпапке
    // SHiNE-promo-solana-devnet, где собраны его собственные задачи и зависимости.
    workingDir = file('SHiNE-promo-solana-devnet')
    commandLine 'bash', '-lc', './gradlew deployToServer'
}

tasks.named('startLocal').configure {
    mustRunAfter tasks.named('build')
}
