"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.populateReleaseInstallerAssets = populateReleaseInstallerAssets;
exports.renderPosixInstallerAsset = renderPosixInstallerAsset;
exports.shellQuote = shellQuote;
exports.renderWindowsInstallerAsset = renderWindowsInstallerAsset;
exports.renderPosixUninstallerAsset = renderPosixUninstallerAsset;
exports.renderWindowsUninstallerAsset = renderWindowsUninstallerAsset;
exports.powershellQuote = powershellQuote;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const cli_constants_1 = require("../../../lib/config/cli-constants");
const errors_1 = require("../../../lib/errors");
const output_lifecycle_manifest_1 = require("./output-lifecycle-manifest");
const output_lifecycle_output_state_1 = require("./output-lifecycle-output-state");
const output_lifecycle_staging_1 = require("./output-lifecycle-staging");
function populateReleaseInstallerAssets(options) {
    if (options.state.versionDirectory === null || options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)('Release installer assets require a resolved version directory.', [
            'Exit behavior: exits before generated installer assets are created.',
        ]);
    }
    const origin = (0, output_lifecycle_manifest_1.normalizeDownloadOrigin)(options.officialOrigin ?? output_lifecycle_output_state_1.DEFAULT_RELEASE_DOWNLOAD_ORIGIN);
    const bundleDirectory = (0, output_lifecycle_staging_1.resolveReleaseDownloadBundleDirectory)(options.state);
    const bundleDirectoryRelative = (0, output_lifecycle_staging_1.resolveReleaseDownloadBundleRelativeDirectory)(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)('Release installer assets require a resolved download bundle directory.', [
            'Exit behavior: exits before generated installer assets are created.',
        ]);
    }
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    const posixInstallerPath = node_path_1.default.join(bundleDirectory, cli_constants_1.RELEASE_POSIX_INSTALLER_FILENAME);
    node_fs_1.default.writeFileSync(posixInstallerPath, renderPosixInstallerAsset({
        defaultVersion: options.version,
        installedCommandName: options.installedCommandName,
        officialOrigin: origin,
    }));
    const windowsInstallerPath = node_path_1.default.join(bundleDirectory, cli_constants_1.RELEASE_WINDOWS_INSTALLER_FILENAME);
    node_fs_1.default.writeFileSync(windowsInstallerPath, renderWindowsInstallerAsset({
        defaultVersion: options.version,
        installedCommandName: options.installedCommandName,
        officialOrigin: origin,
    }));
    const posixUninstallerPath = node_path_1.default.join(bundleDirectory, cli_constants_1.RELEASE_POSIX_UNINSTALLER_FILENAME);
    node_fs_1.default.writeFileSync(posixUninstallerPath, renderPosixUninstallerAsset({
        installedCommandName: options.installedCommandName,
    }));
    const windowsUninstallerPath = node_path_1.default.join(bundleDirectory, cli_constants_1.RELEASE_WINDOWS_UNINSTALLER_FILENAME);
    node_fs_1.default.writeFileSync(windowsUninstallerPath, renderWindowsUninstallerAsset({
        installedCommandName: options.installedCommandName,
    }));
    return {
        status: 'created',
        origin,
        assets: [
            {
                target: 'posix-installer',
                filename: cli_constants_1.RELEASE_POSIX_INSTALLER_FILENAME,
                path: `${bundleDirectoryRelative}/${cli_constants_1.RELEASE_POSIX_INSTALLER_FILENAME}`,
                lineEndings: 'lf',
            },
            {
                target: 'windows-installer',
                filename: cli_constants_1.RELEASE_WINDOWS_INSTALLER_FILENAME,
                path: `${bundleDirectoryRelative}/${cli_constants_1.RELEASE_WINDOWS_INSTALLER_FILENAME}`,
                lineEndings: 'crlf',
            },
            {
                target: 'posix-installer',
                filename: cli_constants_1.RELEASE_POSIX_UNINSTALLER_FILENAME,
                path: `${bundleDirectoryRelative}/${cli_constants_1.RELEASE_POSIX_UNINSTALLER_FILENAME}`,
                lineEndings: 'lf',
            },
            {
                target: 'windows-installer',
                filename: cli_constants_1.RELEASE_WINDOWS_UNINSTALLER_FILENAME,
                path: `${bundleDirectoryRelative}/${cli_constants_1.RELEASE_WINDOWS_UNINSTALLER_FILENAME}`,
                lineEndings: 'crlf',
            },
        ],
    };
}
function renderPosixInstallerAsset(options) {
    return [
        '#!/usr/bin/env sh',
        'set -eu',
        '',
        `DAY_SHIFT_DEFAULT_VERSION=${shellQuote(options.defaultVersion)}`,
        `DAY_SHIFT_COMMAND_NAME=${shellQuote(options.installedCommandName)}`,
        `DAY_SHIFT_ORIGIN=${shellQuote(options.officialOrigin)}`,
        'DAY_SHIFT_VERSION=${DAY_SHIFT_VERSION:-$DAY_SHIFT_DEFAULT_VERSION}',
        `DAY_SHIFT_INSTALL_DIR=\${DAY_SHIFT_INSTALL_DIR:-${cli_constants_1.DEFAULT_POSIX_INSTALL_DIRECTORY}}`,
        `DAY_SHIFT_BIN_DIR=\${DAY_SHIFT_BIN_DIR:-${cli_constants_1.DEFAULT_POSIX_BIN_DIRECTORY}}`,
        '',
        'log() {',
        '  printf \'[day-shift] %s\\n\' "$1"',
        '}',
        '',
        'fail() {',
        '  printf \'[day-shift] ERROR: %s\\n\' "$1" >&2',
        '  exit 1',
        '}',
        '',
        'begin_step() {',
        '  current_step=$1',
        '  step_started_at=$(date +%s)',
        '  log "$current_step..."',
        '}',
        '',
        'finish_step() {',
        '  step_finished_at=$(date +%s)',
        '  step_elapsed=$((step_finished_at - step_started_at))',
        '  log "$current_step complete (${step_elapsed}s)."',
        '}',
        '',
        'download_file() {',
        '  download_label=$1',
        '  download_url=$2',
        '  download_destination=$3',
        '  begin_step "$download_label"',
        '  log "Source: $download_url"',
        '  if ! curl --fail --show-error --location --progress-bar --connect-timeout 15 --max-time 600 --speed-limit 1024 --speed-time 30 "$download_url" -o "$download_destination"; then',
        '    fail "$download_label failed from $download_url. Check the network connection and DAY_SHIFT_ORIGIN, then retry."',
        '  fi',
        '  finish_step',
        '}',
        '',
        'need_command() {',
        '  command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"',
        '}',
        '',
        'version_ge_20() {',
        "  node_version=$(node --version 2>/dev/null | sed 's/^v//')",
        '  node_major=${node_version%%.*}',
        '  case "$node_major" in',
        "    ''|*[!0-9]*) return 1 ;;",
        '  esac',
        '  [ "$node_major" -ge 20 ]',
        '}',
        '',
        'log "Starting Day Shift installation (requested version: $DAY_SHIFT_VERSION)."',
        "begin_step 'Checking prerequisites'",
        'need_command node',
        'need_command tar',
        'need_command shasum',
        "version_ge_20 || fail 'Day Shift requires system Node.js >=20.0.0 before installation changes are made.'",
        'finish_step',
        '',
        'resolve_script_dir() {',
        '  script_path=$0',
        '  while [ -L "$script_path" ]; do',
        '    script_dir=$(CDPATH= cd -- "$(dirname -- "$script_path")" && pwd)',
        '    link_target=$(readlink "$script_path")',
        '    case "$link_target" in',
        '      /*) script_path=$link_target ;;',
        '      *) script_path=$script_dir/$link_target ;;',
        '    esac',
        '  done',
        '  CDPATH= cd -- "$(dirname -- "$script_path")" && pwd',
        '}',
        '',
        'validate_installer_manifest_json() {',
        '  manifest_candidate_path=$1',
        '  manifest_candidate_source=$2',
        "  node -e \"const fs=require('fs'); const manifestPath=process.argv[1]; const manifestSource=process.argv[2]; let text=''; try { text=fs.readFileSync(manifestPath,'utf8'); } catch (error) { console.error('Failed to read downloaded installer manifest.'); process.exit(1); } try { const parsed=JSON.parse(text); if(!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('shape'); } catch (error) { const trimmed=text.trimStart(); const likelyHtml=/^<!doctype html[>\\s]/i.test(trimmed) || /^<html[>\\s]/i.test(trimmed); const reason=likelyHtml ? 'The download returned HTML instead of JSON.' : 'The download did not contain valid JSON.'; console.error('Downloaded installer manifest from ' + manifestSource + ' could not be parsed. ' + reason); console.error('Check that DAY_SHIFT_ORIGIN points at the published downloads directory and that " +
            cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME +
            " is reachable there.'); process.exit(1); }\" \"$manifest_candidate_path\" \"$manifest_candidate_source\" || fail 'Downloaded installer manifest is invalid.'",
        '}',
        '',
        'work_dir=$(mktemp -d 2>/dev/null || mktemp -d -t day-shift-install)',
        'cleanup() { rm -rf "$work_dir"; }',
        'trap cleanup EXIT INT TERM',
        '',
        'script_dir=$(resolve_script_dir)',
        `local_manifest_path="$script_dir/${cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME}"`,
        `manifest_path="$work_dir/${cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME}"`,
        "manifest_source=''",
        'use_local_release_assets=false',
        'if [ -f "$local_manifest_path" ]; then',
        '  manifest_path="$local_manifest_path"',
        '  manifest_source="$local_manifest_path"',
        '  use_local_release_assets=true',
        '  log "Using bundled installer manifest: $manifest_source"',
        'else',
        '  need_command curl',
        '  manifest_url="${DAY_SHIFT_ORIGIN%/}/' + cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME + '"',
        '  if [ "$DAY_SHIFT_VERSION" != latest ]; then',
        '    manifest_url="${DAY_SHIFT_ORIGIN%/}/$DAY_SHIFT_VERSION/' + cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME + '"',
        '  fi',
        "  download_file 'Downloading installer manifest' \"$manifest_url\" \"$manifest_path\"",
        '  manifest_source="$manifest_url"',
        'fi',
        "begin_step 'Validating installer manifest'",
        'validate_installer_manifest_json "$manifest_path" "$manifest_source"',
        '',
        "resolved_version=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); if(!m.version || m.version === 'latest') process.exit(2); process.stdout.write(m.version);\" \"$manifest_path\")",
        "archive_filename=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a) process.exit(2); process.stdout.write(a.filename);\" \"$manifest_path\")",
        "archive_sha256=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a || !/^[a-f0-9]{64}$/.test(a.sha256)) process.exit(2); process.stdout.write(a.sha256);\" \"$manifest_path\")",
        'manifest_command=$(node -e "const fs=require(\'fs\'); const m=JSON.parse(fs.readFileSync(process.argv[1],\'utf8\')); if(!m.command || !m.command.installedName) process.exit(2); process.stdout.write(m.command.installedName);" "$manifest_path")',
        '[ "$manifest_command" = "$DAY_SHIFT_COMMAND_NAME" ] || DAY_SHIFT_COMMAND_NAME=$manifest_command',
        'finish_step',
        'log "Resolved Day Shift $resolved_version ($archive_filename)."',
        '',
        'archive_path="$work_dir/$archive_filename"',
        'extract_dir="$work_dir/extract"',
        'stage_dir="$work_dir/stage"',
        'if [ "$use_local_release_assets" = true ]; then',
        '  [ "$DAY_SHIFT_VERSION" = latest ] || [ "$DAY_SHIFT_VERSION" = "$resolved_version" ] || fail "Local installer assets are for version $resolved_version, but DAY_SHIFT_VERSION requested $DAY_SHIFT_VERSION."',
        '  archive_path="$script_dir/$archive_filename"',
        `  [ -f "$archive_path" ] || fail "Local installer expected archive '$archive_filename' next to ${cli_constants_1.RELEASE_POSIX_INSTALLER_FILENAME}."`,
        '  log "Using bundled release archive: $archive_path"',
        'else',
        '  archive_url="${DAY_SHIFT_ORIGIN%/}/$resolved_version/$archive_filename"',
        "  download_file 'Downloading release archive' \"$archive_url\" \"$archive_path\"",
        'fi',
        "begin_step 'Verifying release archive checksum'",
        'printf \'%s  %s\\n\' "$archive_sha256" "$archive_path" | shasum -a 256 -c - >/dev/null',
        'finish_step',
        "begin_step 'Extracting release archive'",
        'mkdir -p "$extract_dir" "$stage_dir"',
        'tar -xzf "$archive_path" -C "$extract_dir"',
        'payload_root=$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)',
        '[ -n "$payload_root" ] || fail \'Downloaded archive did not contain an installable payload directory.\'',
        'finish_step',
        "begin_step 'Staging release files'",
        'cp -R "$payload_root/." "$stage_dir/"',
        'finish_step',
        "begin_step 'Activating installation'",
        'mkdir -p "$DAY_SHIFT_INSTALL_DIR" "$DAY_SHIFT_BIN_DIR"',
        'rm -rf "$DAY_SHIFT_INSTALL_DIR.previous"',
        'if [ -e "$DAY_SHIFT_INSTALL_DIR/current" ]; then',
        '  mv "$DAY_SHIFT_INSTALL_DIR/current" "$DAY_SHIFT_INSTALL_DIR.previous"',
        'fi',
        'mv "$stage_dir" "$DAY_SHIFT_INSTALL_DIR/current"',
        'ln -sf "$DAY_SHIFT_INSTALL_DIR/current/bin/$DAY_SHIFT_COMMAND_NAME" "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"',
        'finish_step',
        'printf \'Installed Day Shift %s\\n\' "$resolved_version"',
        'printf \'Executable: %s\\n\' "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"',
        'printf \'Command: %s\\n\' "$DAY_SHIFT_COMMAND_NAME"',
        'printf \'If %s is not found, run: export PATH="%s:$PATH"\\n\' "$DAY_SHIFT_COMMAND_NAME" "$DAY_SHIFT_BIN_DIR"',
        "printf 'Persist PATH in your shell profile (for example ~/.zshrc or ~/.bashrc), then open a new shell or source the profile.\\n'",
        '',
    ].join('\n');
}
function shellQuote(value) {
    return `'${value.replace(/'/g, "'\\''")}'`;
}
function renderWindowsInstallerAsset(options) {
    return [
        "$ErrorActionPreference = 'Stop'",
        '',
        `[string]$DayShiftDefaultVersion = ${powershellQuote(options.defaultVersion)}`,
        `[string]$DayShiftCommandName = ${powershellQuote(options.installedCommandName)}`,
        `[string]$DayShiftOrigin = ${powershellQuote(options.officialOrigin)}`,
        '$DayShiftVersion = if ($env:DAY_SHIFT_VERSION) { $env:DAY_SHIFT_VERSION } else { $DayShiftDefaultVersion }',
        "$DayShiftInstallDir = if ($env:DAY_SHIFT_INSTALL_DIR) { $env:DAY_SHIFT_INSTALL_DIR } else { Join-Path $HOME 'AppData\\Local\\DayShift' }",
        "$DayShiftBinDir = if ($env:DAY_SHIFT_BIN_DIR) { $env:DAY_SHIFT_BIN_DIR } else { Join-Path $HOME 'AppData\\Local\\Microsoft\\WindowsApps' }",
        '',
        'function Fail([string]$Message) {',
        '  Write-Error $Message',
        '  exit 1',
        '}',
        '',
        'function Test-Node20 {',
        '  $nodeVersion = (& node --version 2>$null)',
        '  if (-not $nodeVersion) { return $false }',
        "  $nodeVersion = $nodeVersion.TrimStart('v')",
        "  $major = ($nodeVersion -split '\\.')[0]",
        '  $majorNumber = 0',
        '  if (-not [int]::TryParse($major, [ref]$majorNumber)) { return $false }',
        '  return $majorNumber -ge 20',
        '}',
        '',
        "if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Fail 'Missing required command: node' }",
        "if (-not (Test-Node20)) { Fail 'Day Shift requires system Node.js >=20.0.0 before installation changes are made.' }",
        '',
        "$origin = $DayShiftOrigin.TrimEnd('/')",
        `$manifestUrl = "$origin/${cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME}"`,
        "if ($DayShiftVersion -ne 'latest') {",
        `  $manifestUrl = "$origin/$DayShiftVersion/${cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME}"`,
        '}',
        '',
        "$workDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString('N'))",
        'New-Item -ItemType Directory -Path $workDir | Out-Null',
        'try {',
        `  $manifestPath = Join-Path $workDir '${cli_constants_1.RELEASE_INSTALLER_MANIFEST_FILENAME}'`,
        '  Invoke-WebRequest -Uri $manifestUrl -OutFile $manifestPath',
        '  $manifest = Get-Content -Raw -Path $manifestPath | ConvertFrom-Json',
        "  if (-not $manifest.version -or $manifest.version -eq 'latest') { Fail 'Manifest did not resolve to an exact version.' }",
        "  $artifact = @($manifest.artifacts) | Where-Object { $_.target -eq 'windows' -and $_.kind -eq 'archive' -and $_.runtimeModel -eq 'system-node' } | Select-Object -First 1",
        "  if (-not $artifact) { Fail 'Manifest does not contain a Windows system-node archive.' }",
        "  if (-not ($artifact.sha256 -match '^[a-f0-9]{64}$')) { Fail 'Manifest archive checksum is missing or malformed.' }",
        '  if ($manifest.command.installedName) { $DayShiftCommandName = $manifest.command.installedName }',
        '  $archiveUrl = "$origin/$($manifest.version)/$($artifact.filename)"',
        '  $archivePath = Join-Path $workDir $artifact.filename',
        "  $extractDir = Join-Path $workDir 'extract'",
        "  $stageDir = Join-Path $workDir 'stage'",
        '  Invoke-WebRequest -Uri $archiveUrl -OutFile $archivePath',
        '  $actualHash = (Get-FileHash -Algorithm SHA256 -Path $archivePath).Hash.ToLowerInvariant()',
        "  if ($actualHash -ne $artifact.sha256) { Fail 'Downloaded archive SHA-256 did not match manifest metadata.' }",
        '  New-Item -ItemType Directory -Path $extractDir, $stageDir | Out-Null',
        '  Expand-Archive -Path $archivePath -DestinationPath $extractDir',
        '  $payloadRoot = Get-ChildItem -Path $extractDir -Directory | Select-Object -First 1',
        "  if (-not $payloadRoot) { Fail 'Downloaded archive did not contain an installable payload directory.' }",
        "  Copy-Item -Path (Join-Path $payloadRoot.FullName '*') -Destination $stageDir -Recurse",
        '  New-Item -ItemType Directory -Path $DayShiftInstallDir, $DayShiftBinDir -Force | Out-Null',
        "  $currentDir = Join-Path $DayShiftInstallDir 'current'",
        "  $previousDir = Join-Path $DayShiftInstallDir 'previous'",
        '  if (Test-Path $previousDir) { Remove-Item -Recurse -Force $previousDir }',
        '  if (Test-Path $currentDir) { Move-Item -Path $currentDir -Destination $previousDir }',
        '  Move-Item -Path $stageDir -Destination $currentDir',
        '  $sourceShim = Join-Path $currentDir "bin\\$DayShiftCommandName.cmd"',
        '  $targetShim = Join-Path $DayShiftBinDir "$DayShiftCommandName.cmd"',
        '  Copy-Item -Path $sourceShim -Destination $targetShim -Force',
        '  Write-Output "Installed Day Shift $($manifest.version)"',
        '  Write-Output "Executable: $targetShim"',
        '  Write-Output "Command: $DayShiftCommandName"',
        '  Write-Output "Add $DayShiftBinDir to PATH if $DayShiftCommandName is not already available."',
        '} finally {',
        '  if (Test-Path $workDir) { Remove-Item -Recurse -Force $workDir }',
        '}',
        '',
    ].join('\r\n');
}
function renderPosixUninstallerAsset(options) {
    return [
        '#!/usr/bin/env sh',
        'set -eu',
        '',
        `DAY_SHIFT_COMMAND_NAME=${shellQuote(options.installedCommandName)}`,
        `DAY_SHIFT_INSTALL_DIR=\${DAY_SHIFT_INSTALL_DIR:-${cli_constants_1.DEFAULT_POSIX_INSTALL_DIRECTORY}}`,
        `DAY_SHIFT_BIN_DIR=\${DAY_SHIFT_BIN_DIR:-${cli_constants_1.DEFAULT_POSIX_BIN_DIRECTORY}}`,
        '',
        'rm -f "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"',
        'rm -rf "$DAY_SHIFT_INSTALL_DIR"',
        'printf \'Removed Day Shift installation directory: %s\\n\' "$DAY_SHIFT_INSTALL_DIR"',
        'printf \'Removed command shim or symlink: %s\\n\' "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"',
        '',
    ].join('\n');
}
function renderWindowsUninstallerAsset(options) {
    return [
        "$ErrorActionPreference = 'Stop'",
        '',
        `[string]$DayShiftCommandName = ${powershellQuote(options.installedCommandName)}`,
        "$DayShiftInstallDir = if ($env:DAY_SHIFT_INSTALL_DIR) { $env:DAY_SHIFT_INSTALL_DIR } else { Join-Path $HOME 'AppData\\Local\\DayShift' }",
        "$DayShiftBinDir = if ($env:DAY_SHIFT_BIN_DIR) { $env:DAY_SHIFT_BIN_DIR } else { Join-Path $HOME 'AppData\\Local\\Microsoft\\WindowsApps' }",
        '',
        '$cmdShimPath = Join-Path $DayShiftBinDir "$DayShiftCommandName.cmd"',
        '$ps1ShimPath = Join-Path $DayShiftBinDir "$DayShiftCommandName.ps1"',
        'if (Test-Path $cmdShimPath) { Remove-Item -Force $cmdShimPath }',
        'if (Test-Path $ps1ShimPath) { Remove-Item -Force $ps1ShimPath }',
        'if (Test-Path $DayShiftInstallDir) { Remove-Item -Recurse -Force $DayShiftInstallDir }',
        'Write-Output "Removed Day Shift installation directory: $DayShiftInstallDir"',
        'Write-Output "Removed command shims from: $DayShiftBinDir"',
        '',
    ].join('\r\n');
}
function powershellQuote(value) {
    return `'${value.replace(/'/g, "''")}'`;
}
