/**
 * SANDCODE hints protocol.
 *
 * CLIs/SDKs gated on SANDCODE=1 may emit a self-closing tag to stderr:
 *
 *   <sandcode-hint type="plugin" value="name@marketplace" />
 *
 * The Bash/PowerShell tools merge stderr into stdout, scan for these tags
 * via {@link extractSandcodeHints}, strip them so the model never sees the
 * tag (zero-token side channel), and forward the parsed hints to
 * {@link maybeRecordPluginHint} (in utils/plugins/hintRecommendation.ts) for
 * one-shot dialog surfacing on the main thread.
 *
 * Session-scoped state lives here:
 *   - hasShownHintThisSession()  one dialog per session, period
 *   - setPendingHint(hint)       last-write-wins; consumed by the React hook
 *
 * An earlier incarnation of this file was deleted in a refactor pass
 * without porting forward — its absence broke BashTool and PowerShellTool
 * imports until this stub was reintroduced.
 */

export type SandcodeHintType = 'plugin'

export type SandcodeHint = {
  type: SandcodeHintType
  value: string
  sourceCommand: string
}

export type ExtractSandcodeHintsResult = {
  stripped: string
  hints: SandcodeHint[]
}

// `<sandcode-hint ... />` — attribute order is not guaranteed, both `type`
// and `value` must be present, the tag is self-closing. Greedy across a
// single line; tags spanning newlines are not supported (CLIs writing
// hints emit them on one line).
const SANDCODE_HINT_TAG = /<sandcode-hint\b([^>]*?)\/>/g
const ATTR_TYPE = /\btype\s*=\s*"([^"]+)"/
const ATTR_VALUE = /\bvalue\s*=\s*"([^"]+)"/

const VALID_HINT_TYPES = new Set<SandcodeHintType>(['plugin'])

function isValidHintType(raw: string): raw is SandcodeHintType {
  return VALID_HINT_TYPES.has(raw as SandcodeHintType)
}

/**
 * Scan stdout for `<sandcode-hint />` tags. Returns the stripped text plus
 * an array of parsed hints. Malformed tags (missing required attrs, unknown
 * type) are still stripped so they never reach the model, but are not
 * reported as hints.
 */
export function extractSandcodeHints(
  stdout: string,
  sourceCommand: string,
): ExtractSandcodeHintsResult {
  if (!stdout.includes('<sandcode-hint')) {
    return { stripped: stdout, hints: [] }
  }

  const hints: SandcodeHint[] = []
  const stripped = stdout.replace(SANDCODE_HINT_TAG, (_, attrs: string) => {
    const typeMatch = ATTR_TYPE.exec(attrs)
    const valueMatch = ATTR_VALUE.exec(attrs)
    if (typeMatch && valueMatch && isValidHintType(typeMatch[1]!)) {
      hints.push({
        type: typeMatch[1]!,
        value: valueMatch[1]!,
        sourceCommand,
      })
    }
    return ''
  })

  return { stripped, hints }
}

// Session state — process-lifetime, not persisted.

let shownThisSession = false
let pendingHint: SandcodeHint | null = null

export function hasShownHintThisSession(): boolean {
  return shownThisSession
}

/** Mark the session as having surfaced a dialog. Called by the React hook
 * after rendering the dialog (success or dismissal both count). */
export function markHintShownThisSession(): void {
  shownThisSession = true
}

/** Last-write-wins. The React hook drains this each render tick; if no
 * consumer ever picks it up, the hint is silently lost on the next push. */
export function setPendingHint(hint: SandcodeHint): void {
  pendingHint = hint
}

export function getPendingHint(): SandcodeHint | null {
  return pendingHint
}

export function clearPendingHint(): void {
  pendingHint = null
}

/** Test-only reset. */
export function _resetSandcodeHintsForTesting(): void {
  shownThisSession = false
  pendingHint = null
}
