pipeline {
    agent any

    parameters {
        string(name: 'ROLLBACK_TAG', defaultValue: '',
               description: 'Khaali = normal build. Purana build number (jaise 2) = seedha us image par rollback, bina rebuild ke')
    }

    environment {
        REGISTRY   = '51.195.4.170:5000'
        IMAGE_NAME = 'swim/demo-app'
        VAULT_ADDR = 'http://vault:8200'
        // Jenkins container ka workspace /var/jenkins_home/... hai, par docker
        // daemon HOST par chalta hai jahan wahi folder /opt/devops/data/jenkins/...
        // par hai. `docker run -v` ko HOST ka path chahiye.
        JENKINS_HOME_CONTAINER = '/var/jenkins_home'
        JENKINS_HOME_HOST      = '/opt/devops/data/jenkins'
        TRIVY_SEVERITY         = 'HIGH,CRITICAL'
    }

    options {
        timestamps()
        buildDiscarder(logRotator(numToKeepStr: '20'))
        timeout(time: 30, unit: 'MINUTES')
        disableConcurrentBuilds()
    }

    stages {

        stage('Init') {
            steps {
                script {
                    // params ko environment block me mat chhuo - wahan null hone par
                    // NPE aata hai. Yahan script block me safe hai.
                    def rb = (params.ROLLBACK_TAG ?: '').trim()
                    env.IS_ROLLBACK = rb ? 'true' : 'false'
                    env.IMAGE_TAG   = rb ? rb : "${env.BUILD_NUMBER}"
                    echo "MODE      : ${env.IS_ROLLBACK == 'true' ? 'ROLLBACK' : 'NORMAL BUILD'}"
                    echo "IMAGE_TAG : ${env.IMAGE_TAG}"
                }
            }
        }

        stage('Checkout') {
            steps {
                checkout scm
                sh 'echo "commit: $(git rev-parse --short HEAD)"'
            }
        }

        stage('Secrets from Vault') {
            steps {
                sh '''
                    set +x
                    VT=$(cat /var/jenkins_secrets/vault_token)
                    # Vault se registry ka password lo - Jenkins credential se NAHI
                    curl -sf -H "X-Vault-Token: $VT" \
                         "${VAULT_ADDR}/v1/swim/data/registry" \
                      | python3 -c "
import sys, json
d = json.load(sys.stdin)['data']['data']
open('.vault-registry.env','w').write(
    'REG_USER=%s\\nREG_PASS=%s\\n' % (d['username'], d['password']))
print('  Vault se registry creds mil gaye (user=%s)' % d['username'])
"
                    chmod 600 .vault-registry.env
                '''
            }
        }

        stage('Test') {
            when { expression { env.IS_ROLLBACK != 'true' } }
            steps {
                sh '''
                    HOST_WS=$(echo "$PWD" | sed "s|^${JENKINS_HOME_CONTAINER}|${JENKINS_HOME_HOST}|")
                    docker run --rm -v "$HOST_WS":/src -w /src python:3.12-slim sh -c "
                        pip install --quiet -r requirements-dev.txt &&
                        python -m pytest tests/ -v
                    "
                '''
            }
        }

        stage('Build image') {
            when { expression { env.IS_ROLLBACK != 'true' } }
            steps {
                sh '''
                    docker build \
                      --build-arg APP_VERSION=${IMAGE_TAG} \
                      -t ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
                      -t ${REGISTRY}/${IMAGE_NAME}:latest .
                '''
            }
        }

        stage('Security scan (Trivy)') {
            when { expression { env.IS_ROLLBACK != 'true' } }
            steps {
                sh '''
                    # Trivy ko docker socket chahiye taaki local image scan kar sake.
                    # Cache volume se dobara download nahi hoga.
                    docker run --rm \
                      -v /var/run/docker.sock:/var/run/docker.sock \
                      -v trivy-cache:/root/.cache/ \
                      aquasec/trivy:0.58.1 image \
                        --severity ${TRIVY_SEVERITY} \
                        --ignore-unfixed \
                        --exit-code 0 \
                        --format table \
                        ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
                '''
            }
        }

        stage('Smoke test image') {
            when { expression { env.IS_ROLLBACK != 'true' } }
            steps {
                sh '''
                    CID=$(docker run -d -P ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG})
                    sleep 5
                    docker exec $CID python -c "
import urllib.request, json
r = json.loads(urllib.request.urlopen('http://127.0.0.1:8000/version').read())
print('version endpoint:', r)
assert r['version'] == '${IMAGE_TAG}', 'version galat!'
"
                    docker rm -f $CID
                '''
            }
        }

        stage('Push to registry') {
            when { expression { env.IS_ROLLBACK != 'true' } }
            steps {
                sh '''
                    set +x
                    . ./.vault-registry.env
                    echo "$REG_PASS" | docker login ${REGISTRY} -u "$REG_USER" --password-stdin
                    docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
                    docker push ${REGISTRY}/${IMAGE_NAME}:latest
                    docker logout ${REGISTRY}
                '''
            }
        }

        stage('Deploy to VM') {
            steps {
                sh '''
                    set +x
                    . ./.vault-registry.env
                    export REG_USER REG_PASS
                    export ANSIBLE_HOST_KEY_CHECKING=False
                    export IMAGE_TAG=${IMAGE_TAG}
                    ansible-galaxy collection install community.docker --force -q || true
                    ansible-playbook -i deploy/inventory.ini deploy/deploy.yml
                '''
            }
        }

        stage('Post-deploy verify') {
            steps {
                sh '''
                    APPVM=$(awk -F= "/ansible_host/{print \\$2}" deploy/inventory.ini | tr -d " ")
                    GOT=$(curl -sf --max-time 10 http://${APPVM}:8000/version | python3 -c "import sys,json;print(json.load(sys.stdin)['version'])")
                    echo "  expected: ${IMAGE_TAG}  |  live app: ${GOT}"
                    [ "$GOT" = "${IMAGE_TAG}" ] || { echo "VERSION MISMATCH - deploy fail"; exit 1; }
                    echo "  DEPLOY VERIFIED"
                '''
            }
        }
    }

    post {
        always {
            script {
                if (env.WORKSPACE) {
                    sh 'rm -f .vault-registry.env || true'
                    sh 'docker image prune -f --filter "until=24h" || true'
                    cleanWs()
                }
            }
        }
        success { echo "BUILD ${env.BUILD_NUMBER} SAFAL -> ${env.REGISTRY}/${env.IMAGE_NAME}:${env.IMAGE_TAG}" }
        failure { echo "BUILD ${env.BUILD_NUMBER} FAIL" }
    }
}
