/**
 * Startup helpers extracted from main.tsx
 *
 * These are standalone utility functions for telemetry, migrations,
 * settings loading, and deferred prefetches.
 */

import { feature } from 'bun:bundle'
import chalk from 'chalk'
import { readFileSync } from 'fs'
import { getContextWindowForModel } from '../utils/context.js'
import { profileCheckpoint } from '../utils/startupProfiler.js'
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'
import { isAnalyticsDisabled } from 'src/services/analytics/config.js'
import { prefetchOfficialMcpUrls } from '../services/mcp/officialRegistry.js'
import { SandboxManager } from '../utils/sandbox/sandbox-adapter.js'
import { getSettingsForSource, getManagedSettingsKeysForLogging, getInitialSettings } from '../utils/settings/settings.js'
import { resetSettingsCache } from '../utils/settings/settingsCache.js'
import { parseSettingSourcesFlag } from '../utils/settings/constants.js'
import { getGlobalConfig, isAutoUpdaterDisabled, saveGlobalConfig } from '../utils/config.js'
import { isRunningWithBun, isInBundledMode } from '../utils/bundledMode.js'
import { hasNodeOption, isEnvTruthy, isBareMode } from '../utils/envUtils.js'
import { logError } from '../utils/log.js'
import { logForDiagnosticsNoPII } from '../utils/diagLogs.js'
import { getIsNonInteractiveSession, getInitialMainLoopModel, getSdkBetas } from '../bootstrap/state.js'
import { checkHasTrustDialogAccepted } from '../utils/config.js'
import { getSystemContext, getUserContext } from '../context.js'
import { getDefaultMainLoopModel, parseUserSpecifiedModel } from '../utils/model/model.js'
import { refreshModelCapabilities } from '../utils/model/modelCapabilities.js'
import { initUser } from '../utils/user.js'
import { getRelevantTips } from '../services/tips/tipRegistry.js'
import { countFilesRoundedRg } from '../utils/ripgrep.js'
import { settingsChangeDetector } from '../utils/settings/changeDetector.js'
import { skillChangeDetector } from '../utils/skills/skillChangeDetector.js'
import { initializeAnalyticsGates } from 'src/services/analytics/sink.js'
import { safeParseJSON } from '../utils/json.js'
import { generateTempFilePath } from '../utils/tempfile.js'
import { writeFileSync_DEPRECATED } from '../utils/slowOperations.js'
import { getFsImplementation, safeResolvePath } from '../utils/fsOperations.js'
import { isENOENT, errorMessage } from 'src/utils/errors.js'
import { eagerParseCliFlag } from 'src/utils/cliArgs.js'
import { getCwd } from 'src/utils/cwd.js'
import { logSkillsLoaded } from '../utils/telemetry/skillLoadedEvent.js'
import { clearPluginCache, loadAllPluginsCacheOnly } from '../utils/plugins/pluginLoader.js'
import { getManagedPluginNames } from '../utils/plugins/managedPlugins.js'
import { getPluginSeedDirs } from '../utils/plugins/pluginDirectories.js'
import { logPluginLoadErrors, logPluginsEnabledForSession } from '../utils/telemetry/pluginTelemetry.js'
import { getIsGit, getWorktreeCount } from '../utils/git.js'
import { getGhAuthStatus } from '../utils/github/ghAuthStatus.js'
import { prefetchAwsCredentialsAndBedRockInfoIfSafe, prefetchGcpCredentialsIfSafe } from '../utils/auth.js'
import { migrateAutoUpdatesToSettings } from '../migrations/migrateAutoUpdatesToSettings.js'
import { migrateBypassPermissionsAcceptedToSettings } from '../migrations/migrateBypassPermissionsAcceptedToSettings.js'
import { migrateEnableAllProjectMcpServersToSettings } from '../migrations/migrateEnableAllProjectMcpServersToSettings.js'
import { migrateFennecToOpus } from '../migrations/migrateFennecToOpus.js'
import { migrateLegacyOpusToCurrent } from '../migrations/migrateLegacyOpusToCurrent.js'
import { migrateOpusToOpus1m } from '../migrations/migrateOpusToOpus1m.js'
import { migrateReplBridgeEnabledToRemoteControlAtStartup } from '../migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.js'
import { migrateSonnet1mToSonnet45 } from '../migrations/migrateSonnet1mToSonnet45.js'
import { migrateSonnet45ToSonnet46 } from '../migrations/migrateSonnet45ToSonnet46.js'
import { resetAutoModeOptInForDefaultOffer } from '../migrations/resetAutoModeOptInForDefaultOffer.js'
import { resetProToOpusDefault } from '../migrations/resetProToOpusDefault.js'
import { migrateChangelogFromConfig } from '../utils/releaseNotes.js'
import { setFlagSettingsPath, setAllowedSettingSources } from '../bootstrap/state.js'

// @[MODEL LAUNCH]: Consider any migrations you may need for model strings. See migrateSonnet1mToSonnet45.ts for an example.
// Bump this when adding a new sync migration so existing users re-run the set.
const CURRENT_MIGRATION_VERSION = 11

export function logManagedSettings(): void {
  try {
    const policySettings = getSettingsForSource('policySettings')
    if (policySettings) {
      const allKeys = getManagedSettingsKeysForLogging(policySettings)
      logEvent('tengu_managed_settings_loaded', {
        keyCount: allKeys.length,
        keys: allKeys.join(',') as unknown as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
      })
    }
  } catch {
    // Silently ignore errors - this is just for analytics
  }
}

// Check if running in debug/inspection mode
export function isBeingDebugged(): boolean {
  const isBun = isRunningWithBun()

  const hasInspectArg = process.execArgv.some(arg => {
    if (isBun) {
      return /--inspect(-brk)?/.test(arg)
    } else {
      return /--inspect(-brk)?|--debug(-brk)?/.test(arg)
    }
  })

  const hasInspectEnv =
    process.env.NODE_OPTIONS && /--inspect(-brk)?|--debug(-brk)?/.test(process.env.NODE_OPTIONS)

  try {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const inspector = (global as any).require('inspector')
    const hasInspectorUrl = !!inspector.url()
    return hasInspectorUrl || hasInspectArg || !!hasInspectEnv
  } catch {
    return hasInspectArg || !!hasInspectEnv
  }
}

export function logSessionTelemetry(): void {
  const model = parseUserSpecifiedModel(getInitialMainLoopModel() ?? getDefaultMainLoopModel())
  void logSkillsLoaded(getCwd(), getContextWindowForModel(model, getSdkBetas()))
  void loadAllPluginsCacheOnly()
    .then(({ enabled, errors }) => {
      const managedNames = getManagedPluginNames()
      logPluginsEnabledForSession(enabled, managedNames, getPluginSeedDirs())
      logPluginLoadErrors(errors, managedNames)
    })
    .catch(err => logError(err))
}

export function getCertEnvVarTelemetry(): Record<string, boolean> {
  const result: Record<string, boolean> = {}
  if (process.env.NODE_EXTRA_CA_CERTS) {
    result.has_node_extra_ca_certs = true
  }
  if (process.env.SANDCODE_CLIENT_CERT) {
    result.has_client_cert = true
  }
  if (hasNodeOption('--use-system-ca')) {
    result.has_use_system_ca = true
  }
  if (hasNodeOption('--use-openssl-ca')) {
    result.has_use_openssl_ca = true
  }
  return result
}

export async function logStartupTelemetry(): Promise<void> {
  if (isAnalyticsDisabled()) return
  const [isGit, worktreeCount, ghAuthStatus] = await Promise.all([
    getIsGit(),
    getWorktreeCount(),
    getGhAuthStatus(),
  ])
  logEvent('tengu_startup_telemetry', {
    is_git: isGit,
    worktree_count: worktreeCount,
    gh_auth_status: ghAuthStatus as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
    sandbox_enabled: SandboxManager.isSandboxingEnabled(),
    are_unsandboxed_commands_allowed: SandboxManager.areUnsandboxedCommandsAllowed(),
    is_auto_bash_allowed_if_sandbox_enabled: SandboxManager.isAutoAllowBashIfSandboxedEnabled(),
    auto_updater_disabled: isAutoUpdaterDisabled(),
    prefers_reduced_motion: getInitialSettings().prefersReducedMotion ?? false,
    ...getCertEnvVarTelemetry(),
  })
}

export function runMigrations(): void {
  if (getGlobalConfig().migrationVersion !== CURRENT_MIGRATION_VERSION) {
    migrateAutoUpdatesToSettings()
    migrateBypassPermissionsAcceptedToSettings()
    migrateEnableAllProjectMcpServersToSettings()
    resetProToOpusDefault()
    migrateSonnet1mToSonnet45()
    migrateLegacyOpusToCurrent()
    migrateSonnet45ToSonnet46()
    migrateOpusToOpus1m()
    migrateReplBridgeEnabledToRemoteControlAtStartup()
    if (feature('TRANSCRIPT_CLASSIFIER')) {
      resetAutoModeOptInForDefaultOffer()
    }
    if ("external" === 'ant') {
      migrateFennecToOpus()
    }
    saveGlobalConfig(prev =>
      prev.migrationVersion === CURRENT_MIGRATION_VERSION
        ? prev
        : { ...prev, migrationVersion: CURRENT_MIGRATION_VERSION },
    )
  }
  migrateChangelogFromConfig().catch(() => {
    // Silently ignore migration errors - will retry on next startup
  })
}

function prefetchSystemContextIfSafe(): void {
  const isNonInteractiveSession = getIsNonInteractiveSession()

  if (isNonInteractiveSession) {
    logForDiagnosticsNoPII('info', 'prefetch_system_context_non_interactive')
    void getSystemContext()
    return
  }

  const hasTrust = checkHasTrustDialogAccepted()
  if (hasTrust) {
    logForDiagnosticsNoPII('info', 'prefetch_system_context_has_trust')
    void getSystemContext()
  } else {
    logForDiagnosticsNoPII('info', 'prefetch_system_context_skipped_no_trust')
  }
}

export function startDeferredPrefetches(): void {
  if (
    isEnvTruthy(process.env.SANDCODE_EXIT_AFTER_FIRST_RENDER) ||
    isBareMode()
  ) {
    return
  }

  void initUser()
  void getUserContext()
  prefetchSystemContextIfSafe()
  void getRelevantTips()
  if (
    isEnvTruthy(process.env.SANDCODE_USE_BEDROCK) &&
    !isEnvTruthy(process.env.SANDCODE_SKIP_BEDROCK_AUTH)
  ) {
    void prefetchAwsCredentialsAndBedRockInfoIfSafe()
  }
  if (
    isEnvTruthy(process.env.SANDCODE_USE_VERTEX) &&
    !isEnvTruthy(process.env.SANDCODE_SKIP_VERTEX_AUTH)
  ) {
    void prefetchGcpCredentialsIfSafe()
  }
  void countFilesRoundedRg(getCwd(), AbortSignal.timeout(3000), [])

  void initializeAnalyticsGates()
  void prefetchOfficialMcpUrls()
  void refreshModelCapabilities()

  void settingsChangeDetector.initialize()
  if (!isBareMode()) {
    void skillChangeDetector.initialize()
  }

  if ("external" === 'ant') {
    void import('../utils/eventLoopStallDetector.js').then(m => m.startEventLoopStallDetector())
  }
}

export function loadSettingsFromFlag(settingsFile: string): void {
  try {
    const trimmedSettings = settingsFile.trim()
    const looksLikeJson = trimmedSettings.startsWith('{') && trimmedSettings.endsWith('}')
    let settingsPath: string
    if (looksLikeJson) {
      const parsedJson = safeParseJSON(trimmedSettings)
      if (!parsedJson) {
        process.stderr.write(chalk.red('Error: Invalid JSON provided to --settings\n'))
        process.exit(1)
      }
      settingsPath = generateTempFilePath('claude-settings', '.json', {
        contentHash: trimmedSettings,
      })
      writeFileSync_DEPRECATED(settingsPath, trimmedSettings, 'utf8')
    } else {
      const { resolvedPath: resolvedSettingsPath } = safeResolvePath(getFsImplementation(), settingsFile)
      try {
        readFileSync(resolvedSettingsPath, 'utf8')
      } catch (e) {
        if (isENOENT(e)) {
          process.stderr.write(chalk.red(`Error: Settings file not found: ${resolvedSettingsPath}\n`))
          process.exit(1)
        }
        throw e
      }
      settingsPath = resolvedSettingsPath
    }
    setFlagSettingsPath(settingsPath)
    resetSettingsCache()
  } catch (error) {
    if (error instanceof Error) {
      logError(error)
    }
    process.stderr.write(chalk.red(`Error processing settings: ${errorMessage(error)}\n`))
    process.exit(1)
  }
}

export function loadSettingSourcesFromFlag(settingSourcesArg: string): void {
  try {
    const sources = parseSettingSourcesFlag(settingSourcesArg)
    setAllowedSettingSources(sources)
    resetSettingsCache()
  } catch (error) {
    if (error instanceof Error) {
      logError(error)
    }
    process.stderr.write(chalk.red(`Error processing --setting-sources: ${errorMessage(error)}\n`))
    process.exit(1)
  }
}

export function eagerLoadSettings(): void {
  profileCheckpoint('eagerLoadSettings_start')
  const settingsFile = eagerParseCliFlag('--settings')
  if (settingsFile) {
    loadSettingsFromFlag(settingsFile)
  }
  const settingSourcesArg = eagerParseCliFlag('--setting-sources')
  if (settingSourcesArg !== undefined) {
    loadSettingSourcesFromFlag(settingSourcesArg)
  }
  profileCheckpoint('eagerLoadSettings_end')
}

export function initializeEntrypoint(isNonInteractive: boolean): void {
  if (process.env.SANDCODE_ENTRYPOINT) {
    return
  }
  const cliArgs = process.argv.slice(2)

  const mcpIndex = cliArgs.indexOf('mcp')
  if (mcpIndex !== -1 && cliArgs[mcpIndex + 1] === 'serve') {
    process.env.SANDCODE_ENTRYPOINT = 'mcp'
    return
  }
  if (isEnvTruthy(process.env.SANDCODE_ACTION)) {
    process.env.SANDCODE_ENTRYPOINT = 'sandcode-github-action'
    return
  }

  process.env.SANDCODE_ENTRYPOINT = isNonInteractive ? 'sdk-cli' : 'cli'
}
