/**
 * Plugin install/update/disable CLI command handlers.
 *
 * Thin wrappers around the in-app plugin manager API. Called from
 * cli/handlers/plugins.ts after it parses CLI options + emits analytics;
 * each handler does the actual filesystem/settings work and prints user-
 * facing success/failure lines via console.log/console.error.
 *
 * v0.1: real implementations replacing the no-op stubs from cluster-f-1.
 * - install: parse `name@marketplace`, look up via marketplaceManager,
 *   delegate to installPluginFromMarketplace
 * - uninstall: removeInstalledPlugin + clear settings flag + optionally
 *   wipe cache
 * - enable/disable: flip the settings flag in the right scope
 * - disableAll: walk installed plugins, flip every entry to disabled
 * - update: re-resolve from marketplace + reinstall over the existing entry
 *
 * The scope-validation constants stay live since the commander-js
 * .choices() arrays consume them at registration time in main.tsx.
 */

import type { ConfigScope } from '../mcp/types.js'

export const VALID_INSTALLABLE_SCOPES: ConfigScope[] = ['user', 'project', 'local']
export const VALID_UPDATE_SCOPES: ConfigScope[] = ['user', 'project', 'local']

type PluginScope = 'user' | 'project' | 'local'

function logOk(msg: string): void {
  // biome-ignore lint/suspicious/noConsole: CLI handler intentionally writes to stdout
  console.log(`✓ ${msg}`)
}

function logErr(msg: string): void {
  // biome-ignore lint/suspicious/noConsole: CLI handler intentionally writes to stderr
  console.error(`✕ ${msg}`)
  process.exitCode = 1
}

/**
 * Install a plugin from a marketplace.
 *
 * @param plugin Plugin identifier in the form `name` or `name@marketplace`
 * @param scope  Settings scope to install into ('user' | 'project' | 'local')
 */
export async function installPlugin(plugin: string, scope: PluginScope = 'user'): Promise<void> {
  try {
    const { parsePluginIdentifier } = await import('../../utils/plugins/pluginIdentifier.js')
    const { getPluginById } = await import('../../utils/plugins/marketplaceManager.js')
    const { installPluginFromMarketplace } =
      await import('../../utils/plugins/pluginInstallationHelpers.js')

    const { name, marketplace } = parsePluginIdentifier(plugin)
    if (!name) {
      logErr(`Invalid plugin identifier: ${plugin}`)
      return
    }
    if (!marketplace) {
      logErr(`Plugin identifier needs a marketplace suffix (e.g., ${plugin}@<marketplace>)`)
      return
    }

    const lookup = await getPluginById(plugin)
    if (!lookup) {
      logErr(`Plugin not found in marketplace: ${plugin}`)
      return
    }

    const result = await installPluginFromMarketplace({
      pluginId: plugin,
      entry: lookup.entry,
      marketplaceName: marketplace,
      scope,
      trigger: 'cli',
    })

    // Record the outcome in plugin-trust telemetry. Fire-and-forget so
    // a telemetry write failure never breaks the install CLI.
    void (async () => {
      try {
        const { recordPluginInstall } = await import('../pluginTrust/pluginTrust.js')
        await recordPluginInstall(plugin, result.success)
      } catch {
        // swallow — telemetry is best-effort
      }
    })()

    if (result.success) {
      logOk(`Installed ${plugin} (${scope} scope)`)
    } else {
      logErr(`Install failed: ${result.error ?? 'unknown error'}`)
    }
  } catch (err) {
    // A crash is a failure too — record it so the plugin's trust score
    // reflects this machine's reality.
    void (async () => {
      try {
        const { recordPluginInstall } = await import('../pluginTrust/pluginTrust.js')
        await recordPluginInstall(plugin, false)
      } catch {
        // swallow
      }
    })()
    logErr(`Install crashed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

/**
 * Uninstall a plugin: remove from installed_plugins.json + clear the
 * settings flag. Optional `keepData` preserves the plugin's cache dir.
 */
export async function uninstallPlugin(
  plugin: string,
  scope: PluginScope = 'user',
  keepData = false,
): Promise<void> {
  try {
    const { parsePluginIdentifier } = await import('../../utils/plugins/pluginIdentifier.js')
    const { removeInstalledPlugin, deletePluginCache, isPluginInstalled, loadInstalledPluginsV2 } =
      await import('../../utils/plugins/installedPluginsManager.js')

    const { name } = parsePluginIdentifier(plugin)
    if (!name) {
      logErr(`Invalid plugin identifier: ${plugin}`)
      return
    }

    if (!isPluginInstalled(plugin)) {
      logErr(`Plugin not installed: ${plugin}`)
      return
    }

    // Capture install paths before removal so we can clean cache after.
    const installed = loadInstalledPluginsV2()
    const entries = installed.plugins[plugin] ?? []
    const installPaths = entries.map((e) => e.installPath).filter(Boolean) as string[]

    removeInstalledPlugin(plugin, scope)

    if (!keepData) {
      for (const p of installPaths) {
        try {
          deletePluginCache(p)
        } catch {
          // best effort — file may already be gone
        }
      }
    }

    await flipPluginEnabledFlag(plugin, scope, false)
    logOk(`Uninstalled ${plugin}${keepData ? ' (cache kept)' : ''}`)
  } catch (err) {
    logErr(`Uninstall failed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

export async function enablePlugin(plugin: string, scope?: PluginScope): Promise<void> {
  try {
    await flipPluginEnabledFlag(plugin, scope ?? 'user', true)
    logOk(`Enabled ${plugin} (${scope ?? 'user'} scope)`)
  } catch (err) {
    logErr(`Enable failed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

export async function disablePlugin(plugin: string, scope?: PluginScope): Promise<void> {
  try {
    await flipPluginEnabledFlag(plugin, scope ?? 'user', false)
    logOk(`Disabled ${plugin} (${scope ?? 'user'} scope)`)
  } catch (err) {
    logErr(`Disable failed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

/**
 * Disable every installed plugin in the given scope. Used by the
 * `sandcode plugin disable --all` flag.
 */
export async function disableAllPlugins(scope: PluginScope = 'user'): Promise<void> {
  try {
    const { loadInstalledPluginsV2 } =
      await import('../../utils/plugins/installedPluginsManager.js')
    const installed = loadInstalledPluginsV2()
    const pluginIds = Object.keys(installed.plugins)
    if (pluginIds.length === 0) {
      logOk('No installed plugins to disable')
      return
    }
    for (const id of pluginIds) {
      try {
        await flipPluginEnabledFlag(id, scope, false)
      } catch {
        // best-effort across all plugins; individual errors don't abort
      }
    }
    logOk(`Disabled ${pluginIds.length} plugin(s) in ${scope} scope`)
  } catch (err) {
    logErr(`Disable-all failed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

/**
 * Update a plugin: re-resolve from its marketplace and reinstall over
 * the existing entry. Equivalent to `uninstall && install` but
 * preserves the existing scope.
 */
export async function updatePluginCli(plugin: string, scope: PluginScope = 'user'): Promise<void> {
  try {
    const { parsePluginIdentifier } = await import('../../utils/plugins/pluginIdentifier.js')
    const { getPluginById } = await import('../../utils/plugins/marketplaceManager.js')
    const { installPluginFromMarketplace } =
      await import('../../utils/plugins/pluginInstallationHelpers.js')

    const { marketplace } = parsePluginIdentifier(plugin)
    if (!marketplace) {
      logErr(`Plugin identifier needs a marketplace suffix: ${plugin}`)
      return
    }

    const lookup = await getPluginById(plugin)
    if (!lookup) {
      logErr(`Plugin not found in marketplace: ${plugin}`)
      return
    }

    const result = await installPluginFromMarketplace({
      pluginId: plugin,
      entry: lookup.entry,
      marketplaceName: marketplace,
      scope,
      trigger: 'cli',
    })

    if (result.success) {
      logOk(`Updated ${plugin} (${scope} scope)`)
    } else {
      logErr(`Update failed: ${result.error ?? 'unknown error'}`)
    }
  } catch (err) {
    logErr(`Update crashed: ${err instanceof Error ? err.message : String(err)}`)
  }
}

// ─── helpers ──────────────────────────────────────────────────────────────

/**
 * Flip the `plugins[<id>].enabled` boolean in the requested settings scope.
 * Settings reads are cached and auto-bust on write, so the next CLI
 * invocation sees the change immediately.
 */
async function flipPluginEnabledFlag(
  pluginId: string,
  scope: PluginScope,
  enabled: boolean,
): Promise<void> {
  const settingsModule = await import('../../utils/settings/settings.js')
  const source =
    scope === 'user' ? 'userSettings' : scope === 'project' ? 'projectSettings' : 'localSettings'

  const getSettingsForSource = (settingsModule as Record<string, unknown>).getSettingsForSource as
    | ((s: string) => Record<string, unknown> | undefined)
    | undefined
  const saveSettingsForSource = (settingsModule as Record<string, unknown>)
    .saveSettingsForSource as ((s: string, v: unknown) => Promise<void>) | undefined

  if (!getSettingsForSource || !saveSettingsForSource) {
    throw new Error(
      'settings/settings.js does not export getSettingsForSource/saveSettingsForSource — ' +
        'cannot toggle plugin enabled flag from the CLI in this build',
    )
  }

  const current = (getSettingsForSource(source) ?? {}) as Record<string, unknown>
  const plugins = ((current.plugins as Record<string, { enabled?: boolean }>) ?? {}) as Record<
    string,
    { enabled?: boolean }
  >
  plugins[pluginId] = { ...(plugins[pluginId] ?? {}), enabled }
  await saveSettingsForSource(source, { ...current, plugins })
}
