pipeline {
    agent any

    environment {
        GIT_REPO = 'ssh://git@git.jasonpoage.com/jason/express-blog.git'
        DEPLOY_BASE = '/srv/jasonpoage.com'
        LOG_DIR = "${DEPLOY_BASE}/logs"
        TIMESTAMP = sh(script: "date +%Y%m%d-%H%M%S", returnStdout: true).trim()
    }

    options {
        timestamps()
        ansiColor('xterm')
    }

    parameters {
        string(name: 'DEPLOY_BRANCH', defaultValue: 'main', description: 'Branch to deploy (testing, staging, main, production only)')
        string(name: 'OLD_REV', defaultValue: '', description: 'Old git revision (optional)')
        string(name: 'NEW_REV', defaultValue: '', description: 'New git revision (optional)')
    }

    stages {

        stage('Validate Branch') {
            steps {
                script {
                    def allowed = ['testing', 'staging', 'main', 'production']
                    if (!allowed.contains(params.DEPLOY_BRANCH)) {
                        error "Branch '${params.DEPLOY_BRANCH}' is not allowed for deployment."
                    }
                }
            }
        }

        stage('Clone to Tempdir') {
            steps {
                script {
                    env.TMPDIR = sh(script: "mktemp -d", returnStdout: true).trim()
                    sh """
                        git clone --branch '${params.DEPLOY_BRANCH}' '${GIT_REPO}' '${TMPDIR}'
                    """
                }
            }
        }

        stage('Copy and Source .env') {
            steps {
                script {
                    env.ENV_FILE = "${DEPLOY_BASE}/${params.DEPLOY_BRANCH}.env"
                    env.LOG_FILE = "${LOG_DIR}/receive-${env.TIMESTAMP}.log"

                    sh """
                        cp '${ENV_FILE}' '${TMPDIR}/.env'
                    """

                    def envVars = sh(
                        script: "set -a && source '${TMPDIR}/.env' && env | grep -E '^(SERVER_SCHEMA|SERVER_DOMAIN)='",
                        returnStdout: true
                    ).trim().split("\n")

                    envVars.each {
                        def (key, value) = it.tokenize('=')
                        env[key] = value
                    }
                }
            }
        }

        stage('Initialize Submodules') {
            steps {
                sh """
                    git --git-dir='${TMPDIR}/.git' --work-tree='${TMPDIR}' submodule update --init --recursive
                """
            }
        }

        stage('Install Dependencies') {
            steps {
                script {
                    def skipInstall = false
                    if (params.OLD_REV && params.NEW_REV && params.OLD_REV != "0000000000000000000000000000000000000000") {
                        def changed = sh(
                            script: "git --git-dir='${TMPDIR}/.git' diff-tree --name-only -r ${params.OLD_REV}..${params.NEW_REV}",
                            returnStdout: true
                        )
                        if (!changed.contains('package.json') && !changed.contains('yarn.lock')) {
                            skipInstall = true
                        }
                    }

                    if (!skipInstall) {
                        sh """
                            cd '${TMPDIR}'
                            yarn
                        """
                    } else {
                        echo "No dependency changes detected. Skipping yarn install."
                    }
                }
            }
        }

        stage('Build CSS') {
            steps {
                sh """
                    cd '${TMPDIR}'
                    yarn --production=false combine:css
                """
            }
        }

        stage('Start Application for Test') {
            steps {
                script {
                    env.PIDFILE = "${TMPDIR}/test.pid"
                    sh """
                        systemctl --user stop express-blog@${params.DEPLOY_BRANCH}.service || true
                        cd '${TMPDIR}'
                        nohup node src/app.js >> '${LOG_FILE}' 2>&1 &
                        echo \$! > '${PIDFILE}'
                    """
                }
            }
        }

        stage('Wait for Service Readiness') {
            steps {
                script {
                    def timeout = 30
                    def elapsed = 0
                    def success = false
                    while (elapsed < timeout) {
                        def result = sh(script: "curl --silent --fail '${env.SERVER_SCHEMA}://${env.SERVER_DOMAIN}' > /dev/null || true", returnStatus: true)
                        if (result == 0) {
                            success = true
                            break
                        }
                        sleep 1
                        elapsed += 1
                    }
                    if (!success) {
                        sh "cat '${LOG_FILE}'"
                        error "Service did not become available within ${timeout}s."
                    }
                }
            }
        }

        stage('Run Tests') {
            steps {
                script {
                    def testStatus = sh(script: "cd '${TMPDIR}' && npm run test:postreceive", returnStatus: true)
                    if (testStatus != 0) {
                        sh "kill \$(cat '${PIDFILE}') || true"
                        sh "cat '${LOG_FILE}'"
                        error "Tests failed for branch ${params.DEPLOY_BRANCH}"
                    }
                }
            }
        }

        stage('Stop Test App') {
            steps {
                sh "kill \$(cat '${PIDFILE}') || true"
            }
        }

        stage('Deploy') {
            steps {
                script {
                    def DEPLOY_PATH = "${DEPLOY_BASE}/expressjs-blog-${params.DEPLOY_BRANCH}"
                    if (params.DEPLOY_BRANCH == 'testing') {
                        sh """
                            rm -rf '${DEPLOY_PATH}' || true
                            mv '${TMPDIR}' '${DEPLOY_PATH}'
                            git config -f ${DEPLOY_BASE}/expressjs-blog.git/modules/content/config core.worktree '${DEPLOY_BASE}/expressjs-blog-testing/content'
                            ln -f '${ENV_FILE}' '${DEPLOY_PATH}/.env'
                        """
                    } else {
                        def dirExists = sh(script: "[ -d '${DEPLOY_PATH}' ] && echo 1 || echo 0", returnStdout: true).trim()
                        if (dirExists == "0") {
                            sh "git clone --branch '${params.DEPLOY_BRANCH}' '${GIT_REPO}' '${DEPLOY_PATH}'"
                        }

                        sh """
                            cd '${DEPLOY_PATH}'
                            git fetch origin
                            git reset --hard 'origin/${params.DEPLOY_BRANCH}'
                            git submodule update --init --recursive --force
                            ln -f '${ENV_FILE}' '${DEPLOY_PATH}/.env'
                        """

                        def skipInstall = false
                        if (params.OLD_REV && params.NEW_REV && params.OLD_REV != "0000000000000000000000000000000000000000") {
                            def changed = sh(
                                script: "git --git-dir='${DEPLOY_PATH}/.git' diff-tree --name-only -r ${params.OLD_REV}..${params.NEW_REV}",
                                returnStdout: true
                            )
                            if (!changed.contains('package.json') && !changed.contains('yarn.lock')) {
                                skipInstall = true
                            }
                        }

                        if (!skipInstall) {
                            sh "cd '${DEPLOY_PATH}' && yarn"
                        } else {
                            echo "No dependency changes detected. Skipping yarn install."
                        }

                        sh "cd '${DEPLOY_PATH}' && yarn combine:css"
                    }
                }
            }
        }

        stage('Restart Service') {
            steps {
                sh "systemctl --user restart express-blog@${params.DEPLOY_BRANCH}.service"
            }
        }
    }

    post {
        success {
            echo "Deployment of ${params.DEPLOY_BRANCH} completed successfully."
        }
        failure {
            echo "Deployment of ${params.DEPLOY_BRANCH} failed."
        }
        cleanup {
            sh "rm -rf '${TMPDIR}' || true"
        }
    }
}
