pipeline {
  agent any
  options {
    timestamps()
    disableConcurrentBuilds()
  }

  parameters {
    choice(name: 'ENVIRONMENT', choices: ['development', 'main'], description: 'Rama a ejecutar')
    string(name: 'VERSION_MAYOR', defaultValue: '1', description: 'Componente mayor de versión')
  }

  environment {
    REGISTRY        = 'dev.aduanasoft.com:8443'
    IMAGE_NAMESPACE = 'panel-bases-anexo24'
  }

  stages {
    stage('Preflight tools') {
      steps {
        sh '''
          set -euo pipefail
          echo "Node: $(hostname)"
          if ! command -v docker >/dev/null 2>&1; then
            echo "ERROR: este agente no tiene docker instalado."
            echo "Usa un nodo Jenkins con label 'docker' y acceso al daemon."
            exit 1
          fi
          docker --version
        '''
      }
    }

    stage('Checkout') {
      steps {
        checkout([
          $class: 'GitSCM',
          branches: [[name: "*/${params.ENVIRONMENT}"]],
          userRemoteConfigs: [[
            url: 'https://git.aduanasoft.com/ADUANASOFT/PANEL_BASES_ANEXO24.git',
            credentialsId: 'gitea_acazares'
          ]],
          extensions: [[$class: 'CloneOption', depth: 0, shallow: false]]
        ])
      }
    }

    stage('Test') {
      steps {
        sh '''
          set -euxo pipefail
          echo "== Test (svelte-check + tsc) =="

          FE_CONTAINER="panel-bases-test-${BUILD_NUMBER}"

          cleanup() {
            docker rm -f "$FE_CONTAINER" >/dev/null 2>&1 || true
          }
          trap cleanup EXIT

          docker rm -f "$FE_CONTAINER" >/dev/null 2>&1 || true
          docker run -d --name "$FE_CONTAINER" -w /workspace node:20-alpine sleep infinity
          docker exec "$FE_CONTAINER" mkdir -p /workspace
          docker cp "$WORKSPACE/." "$FE_CONTAINER:/workspace"

          docker exec "$FE_CONTAINER" sh -lc '
            set -euxo pipefail
            node --version
            npm config set strict-ssl false
            npm ci --legacy-peer-deps
            npm run check
          '
        '''
      }
    }

    stage('Generate version') {
      steps {
        script {
          def year  = sh(returnStdout: true, script: 'date +%y').trim()
          def month = sh(returnStdout: true, script: 'date +%m').trim()

          if (params.ENVIRONMENT == 'development') {
            def shortHash = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim()
            env.APP_VERSION = "${year}.${month}.${params.VERSION_MAYOR}.${shortHash}"
          } else {
            def lastTag = sh(returnStdout: true, script: 'git describe --tags --abbrev=0 2>/dev/null || true').trim()
            def commitCount = lastTag
              ? sh(returnStdout: true, script: "git rev-list ${lastTag}..HEAD --count").trim()
              : sh(returnStdout: true, script: 'git rev-list --count HEAD').trim()

            if (commitCount == '0') {
              commitCount = '1'
            }
            env.APP_VERSION = "${year}.${month}.${params.VERSION_MAYOR}.${commitCount}"
          }

          echo "VERSION: ${env.APP_VERSION}"
        }
      }
    }

    stage('Docker login') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'harbor-credentials', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD')]) {
          sh '''
            set -euo pipefail
            echo "$HARBOR_PASSWORD" | docker login "$REGISTRY" -u "$HARBOR_USERNAME" --password-stdin
          '''
        }
      }
    }

    stage('Build + push') {
      steps {
        script {
          retry(3) {
            sh '''
              set -euo pipefail
              export DOCKER_BUILDKIT=1
              docker build \
                -t "${REGISTRY}/${IMAGE_NAMESPACE}/app:${APP_VERSION}" \
                -t "${REGISTRY}/${IMAGE_NAMESPACE}/app:latest" \
                -f ./Dockerfile \
                .

              docker push "${REGISTRY}/${IMAGE_NAMESPACE}/app:${APP_VERSION}"
              docker push "${REGISTRY}/${IMAGE_NAMESPACE}/app:latest"
            '''
          }
        }
      }
    }

    stage('Tag release (main)') {
      when {
        expression { params.ENVIRONMENT == 'main' }
      }
      steps {
        withCredentials([usernamePassword(credentialsId: 'gitea_acazares', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) {
          sh '''
            set -euo pipefail
            git -c user.name='Jenkins' -c user.email='jenkins@gitea.local' \
              tag -a "v${APP_VERSION}" -m "Release version ${APP_VERSION}"
            git remote set-url origin "https://${GIT_USERNAME}:${GIT_PASSWORD}@git.aduanasoft.com/ADUANASOFT/PANEL_BASES_ANEXO24.git"
            git push origin "v${APP_VERSION}"
          '''
        }
      }
    }

    stage('Deploy development') {
      when {
        expression { params.ENVIRONMENT == 'development' }
      }
      steps {
        withCredentials([
          usernamePassword(credentialsId: 'harbor-credentials', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD'),
          string(credentialsId: 'panel-server-user', variable: 'PANEL_SERVER_USER'),
          string(credentialsId: 'panel-server-host', variable: 'PANEL_SERVER_HOST'),
          sshUserPrivateKey(
            credentialsId: 'panel-server-ssh',
            keyFileVariable: 'PANEL_SERVER_KEY',
            usernameVariable: 'PANEL_SERVER_SSH_USER',
            passphraseVariable: 'PANEL_SERVER_KEY_PASSPHRASE'
          )
        ]) {
          /*
           * Destino: Windows con OpenSSH Server. El agente Jenkins es Linux (bash).
           * En bash, C:\rutas\ con \ dentro de "..." se corrompen → usar siempre C:/ruta (válido en PowerShell).
           * DOCKER_CONFIG local sin wincred para evitar errores de "logon session".
           * ssh-agent + ssh-add para clave con passphrase.
           * StrictHostKeyChecking=accept-new con known_hosts en workspace para idempotencia.
           */
          sh '''
            set -euo pipefail
            WIN_DOCKER_CFG='C:/Aduanasoft/docker-jenkins-ci'
            WIN_APP_PROD='C:/Aduanasoft/panel_bases_prod'
            chmod 600 "${PANEL_SERVER_KEY}" || true
            eval "$(ssh-agent -s)"
            trap 'kill "${SSH_AGENT_PID}" 2>/dev/null || true' EXIT
            if [ -n "${PANEL_SERVER_KEY_PASSPHRASE}" ]; then
              printf '%s\n' "${PANEL_SERVER_KEY_PASSPHRASE}" | ssh-add "${PANEL_SERVER_KEY}"
            else
              ssh-add "${PANEL_SERVER_KEY}"
            fi
            KNOWN="${WORKSPACE}/.jenkins-panel-known_hosts"
            : > "${KNOWN}"
            SSHOPTS="-o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${KNOWN}"
            SSH_U="${PANEL_SERVER_USER}"
            if [ -z "${SSH_U}" ]; then SSH_U="${PANEL_SERVER_SSH_USER}"; fi

            # Paso 1: inicializar DOCKER_CONFIG limpio (sin credsStore/wincred)
            DOCKER_CFG_PS_TEMPLATE="$(cat <<'EOF'
$ErrorActionPreference = "Stop"
$cfg = "_CFG_"
New-Item -ItemType Directory -Force -Path $cfg | Out-Null
Remove-Item -Path (Join-Path $cfg "config.json") -Force -ErrorAction SilentlyContinue
Set-Content -Path (Join-Path $cfg "config.json") -Value '{"auths":{}}' -Encoding Ascii -NoNewline
EOF
)"
            DOCKER_CFG_PS_CMD="$(printf '%s' "${DOCKER_CFG_PS_TEMPLATE}" | sed "s|_CFG_|${WIN_DOCKER_CFG}|g")"
            DOCKER_CFG_PS_B64="$(printf '%s' "${DOCKER_CFG_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
            ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
              powershell.exe -NoProfile -NonInteractive -EncodedCommand "${DOCKER_CFG_PS_B64}"

            # Paso 2: escribir auth de Harbor directo en config.json (evita docker login + wincred)
            if [ -z "${HARBOR_PASSWORD:-}" ]; then
              echo "ERROR: HARBOR_PASSWORD vacío en Jenkins credentials"
              exit 1
            fi
            set +x
            HARBOR_PASSWORD_B64="$(printf '%s' "${HARBOR_PASSWORD}" | base64 -w 0)"
            DOCKER_AUTH_PS_TEMPLATE="$(cat <<'EOF'
$ErrorActionPreference = "Stop"
$cfg = "_CFG_"
$usr = "_USER_"
$reg = "_REG_"
$pwdB64 = "_PWD_B64_"
$pwd = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($pwdB64))
if ([string]::IsNullOrWhiteSpace($pwd)) { throw "HARBOR_PASSWORD vacio o invalido." }
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${usr}:$pwd"))
$json = "{""auths"":{""$reg"":{""auth"":""$auth""}}}"
Set-Content -Path (Join-Path $cfg "config.json") -Value $json -Encoding Ascii -NoNewline
EOF
)"
            DOCKER_AUTH_PS_CMD="$(printf '%s' "${DOCKER_AUTH_PS_TEMPLATE}" \
              | sed "s|_CFG_|${WIN_DOCKER_CFG}|g" \
              | sed "s|_USER_|${HARBOR_USERNAME}|g" \
              | sed "s|_REG_|${REGISTRY}|g" \
              | sed "s|_PWD_B64_|${HARBOR_PASSWORD_B64}|g")"
            AUTH_PS_B64="$(printf '%s' "${DOCKER_AUTH_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
            ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
              powershell.exe -NoProfile -NonInteractive -EncodedCommand "${AUTH_PS_B64}"

            # Paso 3: pull + up + prune
            DOCKER_COMPOSE_PS_TEMPLATE="$(cat <<'EOF'
$ErrorActionPreference = "Stop"
$env:DOCKER_CONFIG = "_CFG_"
Set-Location "_APP_PROD_"
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
docker image prune -f
EOF
)"
            DOCKER_COMPOSE_PS_CMD="$(printf '%s' "${DOCKER_COMPOSE_PS_TEMPLATE}" \
              | sed "s|_CFG_|${WIN_DOCKER_CFG}|g" \
              | sed "s|_APP_PROD_|${WIN_APP_PROD}|g")"
            COMPOSE_PS_B64="$(printf '%s' "${DOCKER_COMPOSE_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
            set -x
            ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
              powershell.exe -NoProfile -NonInteractive -EncodedCommand "${COMPOSE_PS_B64}"
          '''
        }
      }
    }
  }
}
