def sendReportEmail() {
  def to = (params.REPORT_EMAIL_TO ?: '').trim()
  if (!to) {
    to = (env.REPORT_EMAIL_TO ?: '').trim()
  }
  def cc = (params.REPORT_EMAIL_CC ?: '').trim()
  if (!cc) {
    cc = (env.REPORT_EMAIL_CC ?: '').trim()
  }
  if (!to) {
    echo '未配置 REPORT_EMAIL_TO，跳过邮件。在任务参数或环境变量里填写收件人（逗号分隔）。'
    return
  }
  def subject = fileExists('reports/latest/email-subject.txt')
    ? readFile(encoding: 'UTF-8', file: 'reports/latest/email-subject.txt').trim()
    : "【SPD UI自动化】${params.SUITE} ${currentBuild.currentResult} #${env.BUILD_NUMBER}"
  def body = fileExists('reports/latest/email-summary.html')
    ? readFile(encoding: 'UTF-8', file: 'reports/latest/email-summary.html')
    : "<p>构建 ${currentBuild.currentResult}，未生成摘要。请打开 <a href='${env.BUILD_URL}'>Jenkins</a></p>"
  try {
    emailext(
      to: to,
      cc: cc,
      subject: subject,
      body: body,
      mimeType: 'text/html',
      charset: 'UTF-8',
      attachLog: false,
      attachmentsPattern: 'reports/latest/email-summary.html,reports/latest/email-summary.txt',
      recipientProviders: []
    )
    echo "已发送报告邮件至 ${to}（正文为摘要+报告链接，附件为 email-summary.html）"
  } catch (err) {
    echo "邮件发送失败。请安装 Email Extension 插件，并在 Manage Jenkins → System 配置 SMTP：${err}"
  }
}

/** Windows 节点用 bat（cmd），不用 Git Bash。 */
def runCi() {
  if (isUnix()) {
    sh 'node scripts/run-ci.mjs --suite="$SUITE" --shards="$SHARDS"'
  } else {
    bat 'node scripts\\run-ci.mjs --suite=%SUITE% --shards=%SHARDS%'
  }
}

pipeline {
  agent any

  options {
    timestamps()
    disableConcurrentBuilds()
    timeout(time: 6, unit: 'HOURS')
    buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '10'))
  }

  parameters {
    choice(
      name: 'SUITE',
      choices: ['smoke', 'nightly', 'full', 'center', 'dept', 'invoice', 'trace', 'consume', 'recon', 'cost', 'warn', 'pol', 'exc', 'ana', 'ops', 'reag', 'invoice-trace', 'recon-cost', 'extended', 'quarantine'],
      description: '执行套件（见 config/suites.config.mjs）'
    )
    string(name: 'WORKERS', defaultValue: '3', description: '单进程内并行文件数（同一 spec 不拆开）')
    string(name: 'SHARDS', defaultValue: '1', description: '本机分片进程数；夜间建议 2，总浏览器约 SHARDS×WORKERS')
    string(name: 'RETRIES', defaultValue: '0', description: '整条用例重试次数，默认 0；瞬时加载由操作层重试，勿全局打开')
    booleanParam(name: 'RUN_FULL', defaultValue: false, description: '包含 @full 全列排序（耗时长）')
    string(
      name: 'REPORT_EMAIL_TO',
      defaultValue: '',
      description: '测试报告收件人（同事邮箱，多个用英文逗号）。留空则读任务环境变量 REPORT_EMAIL_TO；都空则不发信'
    )
    string(name: 'REPORT_EMAIL_CC', defaultValue: '', description: '抄送，逗号分隔，可选')
  }

  environment {
    CI = '1'
    SUITE = "${params.SUITE}"
    PW_WORKERS = "${params.WORKERS}"
    SHARDS = "${params.SHARDS}"
    PW_RETRIES = "${params.RETRIES}"
    RUN_FULL = "${params.RUN_FULL ? '1' : '0'}"
    PLAYWRIGHT_BROWSERS_PATH = "${WORKSPACE}/.playwright-browsers"
    SPD_CREDENTIALS_ID = "${env.SPD_CREDENTIALS_ID ?: 'spd-ui-test-account'}"
  }

  stages {
    stage('Prepare') {
      steps {
        script {
          if (isUnix()) {
            sh '''
              set -e
              node -v
              npm -v
              node -e "if (parseInt(process.versions.node.split('.')[0], 10) < 20) process.exit(1)"
            '''
          } else {
            bat '''
              node -v
              npm -v
              node -e "if (parseInt(process.versions.node.split('.')[0], 10) < 20) process.exit(1)"
            '''
          }
        }
      }
    }

    stage('Install') {
      steps {
        script {
          if (isUnix()) {
            sh '''
              set -e
              npm ci
              npx playwright install-deps chromium || true
              npm run playwright:install
            '''
          } else {
            bat '''
              call npm ci
              if errorlevel 1 exit /b 1
              call npm run playwright:install
              if errorlevel 1 exit /b 1
            '''
          }
        }
      }
    }

    stage('Typecheck') {
      steps {
        script {
          if (isUnix()) {
            sh 'npm run typecheck'
          } else {
            bat 'call npm run typecheck'
          }
        }
      }
    }

    stage('Test') {
      steps {
        script {
          if (env.TEST_USER?.trim() || fileExists('.env')) {
            runCi()
          } else {
            withCredentials([
              usernamePassword(
                credentialsId: env.SPD_CREDENTIALS_ID,
                usernameVariable: 'TEST_USER',
                passwordVariable: 'TEST_PASS'
              )
            ]) {
              runCi()
            }
          }
        }
      }
    }
  }

  post {
    always {
      junit allowEmptyResults: true, testResults: 'reports/latest/junit.xml'
      archiveArtifacts artifacts: 'reports/latest/**, reports/ci-*/summary.json, reports/ci-*/meta.txt', allowEmptyArchive: true, fingerprint: true
      script {
        if (fileExists('reports/latest/summary.json')) {
          def descScript = 'node -e "var s=require(\'./reports/latest/summary.json\'); var c=s.counts||{}; console.log(s.suite+\' shards=\'+s.shards+\' workers=\'+s.workers+\' fail=\'+(c.failures||0)+\'/\'+(c.tests||0))"'
          def desc = isUnix()
            ? sh(script: descScript, returnStdout: true).trim()
            : bat(script: "@echo off\n${descScript}", returnStdout: true).trim()
          currentBuild.description = desc
        }
      }
      publishHTML([
        allowMissing: true,
        alwaysLinkToLastBuild: true,
        keepAll: true,
        reportDir: 'reports/latest/playwright-report',
        reportFiles: 'index.html',
        reportName: 'Playwright Report'
      ])
      script {
        if (fileExists('reports/latest/allure-results')) {
          try {
            allure includeProperties: false, jdk: '', results: [[path: 'reports/latest/allure-results']]
          } catch (err) {
            echo "Allure 插件不可用，已保留 reports/latest/allure-report：${err}"
            if (fileExists('reports/latest/allure-report/index.html')) {
              publishHTML([
                allowMissing: true,
                alwaysLinkToLastBuild: true,
                keepAll: true,
                reportDir: 'reports/latest/allure-report',
                reportFiles: 'index.html',
                reportName: 'Allure Report'
              ])
            }
          }
        }
        withEnv(["BUILD_RESULT=${currentBuild.currentResult}"]) {
          if (isUnix()) {
            sh 'node scripts/ci-email-summary.mjs || true'
          } else {
            bat returnStatus: true, script: 'node scripts\\ci-email-summary.mjs'
          }
        }
        sendReportEmail()
      }
    }
  }
}
