import { watch } from 'vue'
import type { IChartingLibraryWidget, IStudyApi } from '../types/tradingview'
import { useIndicatorStore } from '../stores/indicators'
import { useIndicatorTypesStore } from '../stores/indicatorTypes'
import { useChartStore } from '../stores/chart'
import type { IndicatorInstance } from '../stores/indicators'

/**
 * Mapping between pandas-ta indicator function names and TradingView indicator display names.
 * Only includes indicators present in BOTH systems (inner join).
 * Keys are lowercase pandas-ta function names; values are TradingView display strings.
 */
const PANDAS_TA_TO_TV_NAMES: Record<string, string> = {
  // Overlap / Moving Averages (17)
  'sma':        'Moving Average',
  'ema':        'Moving Average Exponential',
  'wma':        'Moving Average Weighted',
  'dema':       'Double EMA',
  'tema':       'Triple EMA',
  'trima':      'Triangular Moving Average',
  'kama':       'Moving Average Adaptive',
  't3':         'T3',
  'hma':        'Hull Moving Average',
  'alma':       'Arnaud Legoux Moving Average',
  'midpoint':   'Midpoint',
  'midprice':   'Midprice',
  'supertrend': 'SuperTrend',
  'ichimoku':   'Ichimoku Cloud',
  'vwap':       'VWAP',
  'vwma':       'VWMA',
  'bbands':     'Bollinger Bands',

  // Momentum (22)
  'rsi':     'Relative Strength Index',
  'macd':    'MACD',
  'stoch':   'Stochastic',
  'stochrsi':'Stochastic RSI',
  'cci':     'Commodity Channel Index',
  'willr':   'Williams %R',
  'mom':     'Momentum',
  'roc':     'Rate Of Change',
  'trix':    'TRIX',
  'cmo':     'Chande Momentum Oscillator',
  'adx':     'Average Directional Index',
  'aroon':   'Aroon',
  'ao':      'Awesome Oscillator',
  'bop':     'Balance of Power',
  'uo':      'Ultimate Oscillator',
  'apo':     'Price Oscillator',
  'mfi':     'Money Flow Index',
  'coppock': 'Coppock Curve',
  'dpo':     'Detrended Price Oscillator',
  'fisher':  'Fisher Transform',
  'rvgi':    'Relative Vigor Index',
  'kst':     'Know Sure Thing',

  // Volatility (3)
  'atr':      'Average True Range',
  'kc':       'Keltner Channels',
  'donchian': 'Donchian Channels',

  // Volume (8)
  'obv':   'On Balance Volume',
  'ad':    'Accumulation/Distribution',
  'adosc': 'Chaikin Oscillator',
  'cmf':   'Chaikin Money Flow',
  'eom':   'Ease of Movement',
  'efi':   "Elder's Force Index",
  'kvo':   'Klinger Oscillator',
  'pvt':   'Price Volume Trend',

  // Statistics / Price Transforms (6)
  'stdev':  'Standard Deviation',
  'linreg': 'Linear Regression Curve',
  'slope':  'Linear Regression Slope',
  'hl2':    'Median Price',
  'hlc3':   'Typical Price',
  'ohlc4':  'Average Price',

  // Trend (3)
  'psar':   'Parabolic SAR',
  'vortex': 'Vortex Indicator',
  'chop':   'Choppiness Index',
}

// Total: 59 indicators

/**
 * Reverse mapping from TradingView display name to pandas-ta function name
 */
const TV_TO_PANDAS_TA_NAMES: Record<string, string> = Object.fromEntries(
  Object.entries(PANDAS_TA_TO_TV_NAMES).map(([k, v]) => [v, k])
)

/**
 * Convert pandas-ta parameters to TradingView inputs
 */
function convertPandasTaParamsToTVInputs(pandasTaName: string, params: Record<string, any>): Record<string, any> {
  const tvInputs: Record<string, any> = {}

  if (pandasTaName === 'bbands') {
    tvInputs.length = params.length || 20
    tvInputs.mult = params.upper_std ?? params.std ?? 2
    tvInputs.source = 'close'
  } else if (pandasTaName === 'macd') {
    tvInputs.fastLength = params.fast || 12
    tvInputs.slowLength = params.slow || 26
    tvInputs.signalLength = params.signal || 9
    tvInputs.source = 'close'
  } else if (pandasTaName === 'stoch') {
    tvInputs.kPeriod = params.k || 14
    tvInputs.dPeriod = params.d || 3
    tvInputs.smoothK = params.smooth_k || 3
  } else if (pandasTaName === 'stochrsi') {
    tvInputs.length = params.length || 14
    tvInputs.rsiLength = params.rsi_length || 14
    tvInputs.kPeriod = params.k || 3
    tvInputs.dPeriod = params.d || 3
  } else if (pandasTaName === 'psar') {
    tvInputs.start = params.af0 || 0.02
    tvInputs.increment = params.af || 0.02
    tvInputs.max = params.max_af || 0.2
  } else if (pandasTaName === 'ichimoku') {
    tvInputs.conversionPeriod = params.tenkan || 9
    tvInputs.basePeriod = params.kijun || 26
    tvInputs.laggingSpanPeriod = params.senkou || 52
  } else if (pandasTaName === 'vwap') {
    tvInputs.anchor = params.anchor || 'D'
  } else if (['apo', 'ppo'].includes(pandasTaName)) {
    tvInputs.fastLength = params.fast || 12
    tvInputs.slowLength = params.slow || 26
    tvInputs.source = 'close'
  } else if (pandasTaName === 'uo') {
    tvInputs.fast = params.fast || 7
    tvInputs.medium = params.medium || 14
    tvInputs.slow = params.slow || 28
  } else if (pandasTaName === 'adosc') {
    tvInputs.fastLength = params.fast || 3
    tvInputs.slowLength = params.slow || 10
  } else if (pandasTaName === 'kc') {
    tvInputs.length = params.length || 20
    tvInputs.mult = params.scalar || 2
  } else {
    // Generic: pass length through; skip source
    if (params.length !== undefined) tvInputs.length = params.length
    for (const [k, v] of Object.entries(params)) {
      if (k === 'length' || k === 'source') continue
      tvInputs[k] = v
    }
  }

  return tvInputs
}

/**
 * Convert TradingView inputs to pandas-ta parameters
 */
function convertTVInputsToPandasTaParams(tvName: string, tvInputs: Record<string, any>): { pandasTaName: string | null, pandasTaParams: Record<string, any> } {
  const pandasTaName = TV_TO_PANDAS_TA_NAMES[tvName] || null
  if (!pandasTaName) {
    console.warn('[Indicators] No pandas-ta mapping for TradingView indicator:', tvName)
    return { pandasTaName: null, pandasTaParams: {} }
  }

  const pandasTaParams: Record<string, any> = {}

  if (pandasTaName === 'bbands') {
    pandasTaParams.length = tvInputs.in_0 ?? tvInputs.length ?? 20
    const std = tvInputs.in_1 ?? tvInputs.mult ?? 2
    pandasTaParams.upper_std = std
    pandasTaParams.lower_std = std
  } else if (pandasTaName === 'macd') {
    pandasTaParams.fast = tvInputs.fastLength ?? 12
    pandasTaParams.slow = tvInputs.slowLength ?? 26
    pandasTaParams.signal = tvInputs.signalLength ?? 9
  } else if (pandasTaName === 'stoch') {
    pandasTaParams.k = tvInputs.kPeriod ?? 14
    pandasTaParams.d = tvInputs.dPeriod ?? 3
    pandasTaParams.smooth_k = tvInputs.smoothK ?? 3
  } else if (pandasTaName === 'stochrsi') {
    pandasTaParams.length = tvInputs.length ?? 14
    pandasTaParams.rsi_length = tvInputs.rsiLength ?? 14
    pandasTaParams.k = tvInputs.kPeriod ?? 3
    pandasTaParams.d = tvInputs.dPeriod ?? 3
  } else if (pandasTaName === 'psar') {
    pandasTaParams.af0 = tvInputs.start ?? 0.02
    pandasTaParams.af = tvInputs.increment ?? 0.02
    pandasTaParams.max_af = tvInputs.max ?? 0.2
  } else if (pandasTaName === 'ichimoku') {
    pandasTaParams.tenkan = tvInputs.conversionPeriod ?? 9
    pandasTaParams.kijun = tvInputs.basePeriod ?? 26
    pandasTaParams.senkou = tvInputs.laggingSpanPeriod ?? 52
  } else if (pandasTaName === 'vwap') {
    pandasTaParams.anchor = tvInputs.anchor ?? 'D'
  } else if (['apo', 'ppo'].includes(pandasTaName)) {
    pandasTaParams.fast = tvInputs.fastLength ?? 12
    pandasTaParams.slow = tvInputs.slowLength ?? 26
  } else if (pandasTaName === 'uo') {
    pandasTaParams.fast = tvInputs.fast ?? 7
    pandasTaParams.medium = tvInputs.medium ?? 14
    pandasTaParams.slow = tvInputs.slow ?? 28
  } else if (pandasTaName === 'adosc') {
    pandasTaParams.fast = tvInputs.fastLength ?? 3
    pandasTaParams.slow = tvInputs.slowLength ?? 10
  } else if (pandasTaName === 'kc') {
    pandasTaParams.length = tvInputs.length ?? 20
    pandasTaParams.scalar = tvInputs.mult ?? 2
  } else {
    // Generic: extract length; skip source
    for (const [k, v] of Object.entries(tvInputs)) {
      if (k === 'source') continue
      pandasTaParams[k] = v
    }
    // Normalise TV in_0 → length for simple single-period indicators
    if (tvInputs.in_0 !== undefined && pandasTaParams.length === undefined) {
      pandasTaParams.length = tvInputs.in_0
      delete pandasTaParams.in_0
    }
  }

  return { pandasTaName, pandasTaParams }
}

/**
 * Setup bidirectional synchronization between TradingView indicators and IndicatorStore
 */
export function useTradingViewIndicators(tvWidget: IChartingLibraryWidget) {
  const indicatorStore = useIndicatorStore()
  const chartStore = useChartStore()

  let isApplyingTVUpdate = false // Prevent circular updates when TV modifies indicators
  let isUpdatingStore = false // Prevent circular updates when we update the store from TV
  let isChartReady = false // Track if chart API is ready
  let isUnmounting = false // Set during cleanup to suppress study_event: remove from widget destruction

  /**
   * Convert TradingView study to our IndicatorInstance format
   */
  function convertTVStudyToIndicator(tvStudy: any, symbol: string, studyId?: string): IndicatorInstance | null {
    try {
      console.log('[Indicators] convertTVStudyToIndicator called with:', { tvStudy, symbol, studyId })
      console.log('[Indicators] Study object keys:', Object.keys(tvStudy))
      console.log('[Indicators] Study object type:', typeof tvStudy)

      // Expose study object to window for debugging
      ;(window as any).__lastTVStudy = tvStudy
      console.log('[Indicators] Study object exposed as window.__lastTVStudy for debugging')

      // Study object has methods - name() gives the indicator type, title() gives display name
      let studyName = 'Unknown'

      // Try tvStudy.study().name() first
      if (typeof tvStudy.study === 'function') {
        try {
          const innerStudy = tvStudy.study()
          if (innerStudy && typeof innerStudy.name === 'function') {
            studyName = innerStudy.name()
            console.log('[Indicators] Called tvStudy.study().name():', studyName)
          }
        } catch (e) {
          console.log('[Indicators] Error calling study().name():', e)
        }
      }

      // Try name() directly if that didn't work
      if (studyName === 'Unknown' && typeof tvStudy.name === 'function') {
        try {
          studyName = tvStudy.name()
          console.log('[Indicators] Called study.name():', studyName)
        } catch (e) {
          console.log('[Indicators] Error calling name():', e)
        }
      }

      // Fallback to title() if name() didn't work
      if (studyName === 'Unknown' && typeof tvStudy.title === 'function') {
        try {
          studyName = tvStudy.title()
          console.log('[Indicators] Called study.title():', studyName)
        } catch (e) {
          console.log('[Indicators] Error calling title():', e)
        }
      }

      // Fallback: metaInfo.name is the registered study name (e.g. dxo_ind_custom_trendflex)
      if (studyName === 'Unknown' || (!studyName.startsWith('dxo_ind_') && !TV_TO_PANDAS_TA_NAMES[studyName])) {
        const metaName = tvStudy._study?.name?.() ||
                         tvStudy._metaInfo?.name ||
                         tvStudy.metaInfo?.()?.name
        if (metaName && typeof metaName === 'string') {
          console.log('[Indicators] Overriding studyName with metaInfo.name:', metaName)
          studyName = metaName
        }
      }

      // Last resort: if still unresolved, check all type descriptions
      if (!studyName.startsWith('dxo_ind_') && !TV_TO_PANDAS_TA_NAMES[studyName]) {
        const indicatorTypesStore = useIndicatorTypesStore()
        const title = typeof tvStudy.title === 'function' ? tvStudy.title() : null
        const desc = tvStudy._metaInfo?.description || tvStudy._metaInfo?.shortDescription
        // Also match against studyName itself (TV returns display name from study().name())
        const matchByDesc = Object.values(indicatorTypesStore.types).find(
          t => t.display_name === studyName || t.display_name === title || t.display_name === desc
        )
        if (matchByDesc) {
          studyName = `dxo_ind_${matchByDesc.pandas_ta_name}`
          console.log('[Indicators] Resolved studyName by description match:', studyName)
        }
      }

      console.log('[Indicators] Study name extracted:', studyName)

      // Handle our named custom indicator studies (dxo_ind_ prefix)
      if (studyName.startsWith('dxo_ind_')) {
        const pandasTaNameFromStudy = studyName.slice('dxo_ind_'.length)
        const indicatorTypesStore = useIndicatorTypesStore()
        const typeEntry = indicatorTypesStore.types[pandasTaNameFromStudy]
        if (!typeEntry) {
          console.log('[Indicators] Custom indicator type not found in store:', pandasTaNameFromStudy)
          return null
        }
        const defaultParams = Object.fromEntries(
          Object.entries(typeEntry.metadata.parameters).map(([k, v]) => [k, v.default])
        )
        const now = Math.floor(Date.now() / 1000)
        return {
          id: studyId || `ind_${Date.now()}`,
          pandas_ta_name: pandasTaNameFromStudy,
          instance_name: typeEntry.display_name,
          parameters: defaultParams,
          tv_study_id: studyId,
          tv_indicator_name: studyName,
          visible: true,
          pane: typeEntry.metadata.pane,
          symbol,
          created_at: now,
          modified_at: now,
          custom_metadata: typeEntry.metadata,
        } as IndicatorInstance
      }

      const pandasTaName = TV_TO_PANDAS_TA_NAMES[studyName]
      console.log('[Indicators] pandas-ta mapping:', studyName, '->', pandasTaName)

      if (!pandasTaName) {
        console.log('[Indicators] TradingView study not mapped to pandas-ta:', studyName)
        console.log('[Indicators] Available mappings:', Object.keys(TV_TO_PANDAS_TA_NAMES).slice(0, 10))
        return null
      }

      // Extract inputs from study - try calling getInputValues() if it's a method
      let tvInputs: any = {}

      if (typeof tvStudy.getInputValues === 'function') {
        try {
          const inputsArray = tvStudy.getInputValues()
          console.log('[Indicators] Called study.getInputValues():', inputsArray)

          // Convert array of {id, value} objects to a simple object
          if (Array.isArray(inputsArray)) {
            tvInputs = {}
            for (const input of inputsArray) {
              if (input.id && input.value !== undefined) {
                tvInputs[input.id] = input.value
              }
            }
            console.log('[Indicators] Converted array inputs to object:', tvInputs)
          } else {
            tvInputs = inputsArray
          }
        } catch (e) {
          console.log('[Indicators] Error calling getInputValues():', e)
        }
      } else {
        tvInputs = tvStudy.inputs || tvStudy._inputs || {}
      }

      console.log('[Indicators] TV inputs:', tvInputs)

      const { pandasTaParams } = convertTVInputsToPandasTaParams(studyName, tvInputs)
      console.log('[Indicators] Converted pandas-ta params:', pandasTaParams)

      const now = Math.floor(Date.now() / 1000)

      const indicator = {
        id: studyId || tvStudy.id || `ind_${Date.now()}`,
        pandas_ta_name: pandasTaName,
        instance_name: `${pandasTaName}_${Date.now()}`,
        parameters: pandasTaParams,
        tv_study_id: studyId || tvStudy.id,
        tv_indicator_name: studyName,
        tv_inputs: tvInputs,
        visible: true,
        pane: 'chart',
        symbol,
        created_at: now,
        modified_at: now
      }

      console.log('[Indicators] Created indicator object:', indicator)
      return indicator
    } catch (error) {
      console.error('[Indicators] Error converting TV study:', error)
      return null
    }
  }

  /**
   * Setup listeners for TradingView indicator events
   */
  function setupIndicatorListeners() {
    try {
      const chart = tvWidget.activeChart()
      if (!chart) {
        console.warn('[Indicators] Active chart not available yet')
        return () => {}
      }
      isChartReady = true

      // Apply any indicators that arrived before chart was ready (e.g. from workspace sync on page load)
      const pendingIndicators = Object.values(indicatorStore.indicators).filter(ind => !ind.tv_study_id)
      if (pendingIndicators.length > 0) {
        console.log('[Indicators] Chart ready, applying', pendingIndicators.length, 'pending indicators from store')
        isApplyingTVUpdate = true
        ;(async () => {
          try {
            for (const indicator of pendingIndicators) {
              await createTVStudy(indicator)
            }
          } finally {
            isApplyingTVUpdate = false
          }
        })()
      }

      console.log('[Indicators] Setting up indicator event subscriptions')
      console.log('[Indicators] Chart ready, widget:', tvWidget)

      // Subscribe to 'study_event' - fires for specific study instance events with ID
      // Callback signature: (studyId: string, eventType: string)
      tvWidget.subscribe('study_event', (studyId: any, eventType?: any) => {
        console.log('[Indicators] ===== STUDY_EVENT FIRED =====')
        console.log('[Indicators] Arg 1 (studyId):', studyId)
        console.log('[Indicators] Arg 2 (eventType):', eventType)

        if (isApplyingTVUpdate) {
          console.log('[Indicators] Ignoring study_event - isApplyingTVUpdate is true')
          return
        }

        const currentSymbol = chartStore.symbol

        // If studyId is actually an object, extract the real values
        let actualStudyId = studyId
        let actualEventType = eventType

        if (typeof studyId === 'object' && studyId !== null) {
          actualStudyId = studyId.id || studyId.studyId
          actualEventType = studyId.type || studyId.event || eventType
        }

        console.log('[Indicators] Parsed - Study ID:', actualStudyId, 'Event type:', actualEventType)

        if (!actualStudyId) {
          console.warn('[Indicators] No study ID found, cannot process event')
          return
        }

        // Filter out events we don't care about
        if (actualEventType && !['create', 'remove'].includes(actualEventType)) {
          console.log('[Indicators] Ignoring event type:', actualEventType)
          return
        }

        // If we have an event type, handle it specifically
        if (actualEventType === 'create') {
          console.log('[Indicators] New indicator created with ID:', actualStudyId)

          // Wait for TradingView to finish initializing the study
          setTimeout(() => {
            try {
              // Guard against feedback loop: if we created this study from the store
              // (via createTVStudy or registerCustomStudy), the store entry already has
              // tv_study_id set by the time this setTimeout fires. Skip in that case.
              const alreadyTracked = Object.values(indicatorStore.indicators).find(
                ind => ind.tv_study_id === actualStudyId
              )
              if (alreadyTracked) {
                console.log('[Indicators] Study already tracked in store, skipping create event:', alreadyTracked.id)
                return
              }

              const study = chart.getStudyById(actualStudyId)
              if (study) {
                console.log('[Indicators] Retrieved new study after timeout')

                const indicator = convertTVStudyToIndicator(study, currentSymbol, actualStudyId)
                if (indicator) {
                  console.log('[Indicators] Adding new indicator to store:', indicator)
                  isUpdatingStore = true
                  indicatorStore.addIndicator(indicator)
                  console.log('[Indicators] Indicator added! Store now has:', Object.keys(indicatorStore.indicators).length, 'indicators')
                  isUpdatingStore = false
                } else {
                  console.warn('[Indicators] Conversion returned null - indicator not supported')
                }
              }
            } catch (error) {
              console.error('[Indicators] Error handling create event:', error)
            }
          }, 0)
        }
        else if (actualEventType === 'remove') {
          console.log('[Indicators] Indicator removed with ID:', actualStudyId)

          if (isUnmounting) {
            console.log('[Indicators] Ignoring study remove during widget destruction')
            return
          }

          const existingIndicator = Object.values(indicatorStore.indicators).find(
            ind => ind.tv_study_id === actualStudyId
          )

          if (existingIndicator) {
            console.log('[Indicators] Removing indicator from store:', existingIndicator.id)
            isUpdatingStore = true
            indicatorStore.removeIndicator(existingIndicator.id)
            console.log('[Indicators] Indicator removed!')
            isUpdatingStore = false
          } else {
            console.log('[Indicators] Indicator not found in store for removal')
          }
        }
        else {
          // No event type provided, just a study ID - check if it exists
          console.log('[Indicators] Event fired with study ID but no type, checking if study exists...')

          try {
            const study = chart.getStudyById(actualStudyId)
            if (study) {
              console.log('[Indicators] Study exists - likely created or modified')

              const existingIndicator = Object.values(indicatorStore.indicators).find(
                ind => ind.tv_study_id === actualStudyId
              )

              if (!existingIndicator) {
                // New indicator
                console.log('[Indicators] New indicator detected, adding to store...')
                const indicator = convertTVStudyToIndicator(study, currentSymbol, actualStudyId)
                if (indicator) {
                  isUpdatingStore = true
                  indicatorStore.addIndicator(indicator)
                  console.log('[Indicators] Indicator added!')
                  isUpdatingStore = false
                }
              } else {
                console.log('[Indicators] Indicator already exists in store')
              }
            }
          } catch (error) {
            // Study doesn't exist - it was removed
            console.log('[Indicators] Study does not exist - checking for removal...')

            const existingIndicator = Object.values(indicatorStore.indicators).find(
              ind => ind.tv_study_id === actualStudyId
            )

            if (existingIndicator) {
              console.log('[Indicators] Removing indicator from store')
              isUpdatingStore = true
              indicatorStore.removeIndicator(existingIndicator.id)
              isUpdatingStore = false
            }
          }
        }

        console.log('[Indicators] ===== STUDY_EVENT COMPLETE =====')
      })

      // Subscribe to 'study_properties_changed' - fires when indicator settings are modified
      // This is the key event for detecting when users edit indicator parameters
      // Callback signature: (studyId: string)
      tvWidget.subscribe('study_properties_changed', (studyId: any) => {
        console.log('[Indicators] ===== STUDY_PROPERTIES_CHANGED FIRED =====')
        console.log('[Indicators] Study ID:', studyId)

        if (isApplyingTVUpdate) {
          console.log('[Indicators] Ignoring properties change - isApplyingTVUpdate is true')
          return
        }

        const currentSymbol = chartStore.symbol

        if (studyId && typeof studyId === 'string') {
          console.log('[Indicators] Study properties changed for ID:', studyId)

          // Wait for TradingView to finish applying the changes
          setTimeout(() => {
            try {
              const study = chart.getStudyById(studyId)
              if (study) {
                console.log('[Indicators] Retrieved updated study')

                // Find existing indicator in store
                const existingIndicator = Object.values(indicatorStore.indicators).find(
                  ind => ind.tv_study_id === studyId
                )

                if (existingIndicator) {
                  console.log('[Indicators] Found existing indicator:', existingIndicator.id)

                  // Custom indicators expose param_* inputs that map to their parameters.
                  // Extract those and update the store (useCustomIndicators will re-fetch data).
                  if (existingIndicator.pandas_ta_name.startsWith('custom_')) {
                    if (typeof study.getInputValues === 'function') {
                      const inputsArray = study.getInputValues()
                      if (Array.isArray(inputsArray)) {
                        const newParams: Record<string, any> = {}
                        for (const input of inputsArray) {
                          if (input.id && input.id.startsWith('param_')) {
                            newParams[input.id.slice(6)] = input.value
                          }
                        }
                        if (Object.keys(newParams).length > 0) {
                          const hasChanged = Object.entries(newParams).some(
                            ([k, v]) => existingIndicator.parameters[k] !== v
                          )
                          if (hasChanged) {
                            console.log('[Indicators] Custom indicator params changed:', newParams)
                            isUpdatingStore = true
                            indicatorStore.updateIndicator(existingIndicator.id, { parameters: newParams })
                            isUpdatingStore = false
                          }
                        }
                      }
                    }
                    return
                  }


                  // Get the study name using study().name()
                  let studyName = 'Unknown'
                  if (typeof study.study === 'function') {
                    const innerStudy = study.study()
                    if (innerStudy && typeof innerStudy.name === 'function') {
                      studyName = innerStudy.name()
                    }
                  }

                  console.log('[Indicators] Study name:', studyName)

                  // Get inputs - try calling getInputValues() if available
                  let tvInputs: any = {}
                  if (typeof study.getInputValues === 'function') {
                    const inputsArray = study.getInputValues()

                    // Convert array of {id, value} objects to a simple object
                    if (Array.isArray(inputsArray)) {
                      tvInputs = {}
                      for (const input of inputsArray) {
                        if (input.id && input.value !== undefined) {
                          tvInputs[input.id] = input.value
                        }
                      }
                      console.log('[Indicators] Converted inputs array to object:', tvInputs)
                    } else {
                      tvInputs = inputsArray
                    }
                  }

                  console.log('[Indicators] TV inputs:', tvInputs)

                  const { pandasTaParams } = convertTVInputsToPandasTaParams(studyName, tvInputs)

                  console.log('[Indicators] Old params:', existingIndicator.parameters)
                  console.log('[Indicators] New params:', pandasTaParams)

                  // Update the store with new parameters
                  if (JSON.stringify(existingIndicator.parameters) !== JSON.stringify(pandasTaParams)) {
                    console.log('[Indicators] Parameters changed, updating store...')
                    isUpdatingStore = true
                    indicatorStore.updateIndicator(existingIndicator.id, {
                      parameters: pandasTaParams,
                      tv_inputs: tvInputs
                    })
                    console.log('[Indicators] Indicator updated in store!')
                    isUpdatingStore = false
                  } else {
                    console.log('[Indicators] Parameters unchanged (might be visual properties only)')
                  }
                } else {
                  console.log('[Indicators] No existing indicator found for properties change, ignoring')
                }
              }
            } catch (error) {
              console.error('[Indicators] Error handling study_properties_changed:', error)
            }
          }, 0)
        } else {
          console.log('[Indicators] study_properties_changed with no study ID, ignoring')
        }

        console.log('[Indicators] ===== STUDY_PROPERTIES_CHANGED COMPLETE =====')
      })

      // Suppress study_event: remove during page unload (TV fires removes for all studies
      // as part of its own cleanup, which would otherwise wipe the persistence store)
      const onBeforeUnload = () => { isUnmounting = true }
      window.addEventListener('beforeunload', onBeforeUnload)

      // Cleanup function
      return () => {
        console.log('[Indicators] Cleaning up event subscriptions')
        isUnmounting = true
        window.removeEventListener('beforeunload', onBeforeUnload)
        try {
          tvWidget.unsubscribe('study_event')
          tvWidget.unsubscribe('study_properties_changed')
        } catch (error) {
          console.error('[Indicators] Error during cleanup:', error)
        }
      }
    } catch (error) {
      console.error('[Indicators] Error setting up indicator listeners:', error)
      return () => {}
    }
  }

  /**
   * Setup watchers for IndicatorStore changes to apply to TradingView
   */
  function setupStoreWatchers() {
    watch(
      () => indicatorStore.indicators,
      async (newIndicators, oldIndicators) => {
        if (isUpdatingStore || !isChartReady) return

        isApplyingTVUpdate = true

        try {
          // Check if widget is ready before accessing activeChart
          if (!tvWidget || typeof tvWidget.activeChart !== 'function') {
            console.log('[Indicators] Widget not ready, skipping store update')
            isApplyingTVUpdate = false
            return
          }

          const chart = tvWidget.activeChart()
          if (!chart) {
            console.log('[Indicators] Chart not available, skipping store update')
            isApplyingTVUpdate = false
            return
          }

          const currentSymbol = chartStore.symbol

          // Find added indicators
          for (const [id, indicator] of Object.entries(newIndicators)) {
            if (indicator.symbol && indicator.symbol !== currentSymbol) continue

            if (!oldIndicators || !oldIndicators[id]) {
              // Indicator was added to store
              await createTVStudy(indicator)
            } else {
              // Check if indicator exists in TradingView
              let study = null
              if (indicator.tv_study_id) {
                try {
                  study = chart.getStudyById(indicator.tv_study_id)
                } catch (e) {
                  // Study doesn't exist
                }
              }

              if (!study) {
                // Indicator is in store but not in TradingView - create it
                await createTVStudy(indicator)
              } else if (JSON.stringify(indicator) !== JSON.stringify(oldIndicators[id])) {
                // Indicator was modified
                updateTVStudy(indicator)
              }
            }
          }

          // Find removed indicators
          if (oldIndicators) {
            for (const [id, indicator] of Object.entries(oldIndicators)) {
              if (indicator.symbol && indicator.symbol !== currentSymbol) continue

              if (!newIndicators[id]) {
                removeTVStudy(indicator)
              }
            }
          }
        } catch (error) {
          console.error('[Indicators] Error applying store changes:', error)
        } finally {
          isApplyingTVUpdate = false
        }
      },
      { deep: true }
    )
  }

  /**
   * Create an indicator in TradingView
   */
  async function createTVStudy(indicator: IndicatorInstance) {
    if (!isChartReady) return

    // Custom indicators (pandas_ta_name starts with "custom_") are handled by
    // useCustomIndicators — they use TV createCustomStudy, not createStudy.
    if (indicator.pandas_ta_name.startsWith('custom_')) return

    try {
      const chart = tvWidget.activeChart()
      if (!chart) return

      const currentSymbol = chartStore.symbol
      const tvName = PANDAS_TA_TO_TV_NAMES[indicator.pandas_ta_name]

      if (!tvName) {
        console.warn('[Indicators] No TradingView mapping for pandas-ta indicator:', indicator.pandas_ta_name)
        return
      }

      const tvInputs = convertPandasTaParamsToTVInputs(indicator.pandas_ta_name, indicator.parameters)

      console.log(`[Indicators] Creating TradingView study: ${tvName} with inputs:`, tvInputs)

      const studyId = await chart.createStudy(tvName, false, false, tvInputs)

      // If TradingView assigned a different ID, update the store
      if (studyId && studyId !== indicator.tv_study_id) {
        isUpdatingStore = true
        await new Promise(resolve => setTimeout(resolve, 0))

        try {
          indicatorStore.updateIndicator(indicator.id, {
            tv_study_id: studyId,
            symbol: indicator.symbol || currentSymbol
          })
        } finally {
          await new Promise(resolve => setTimeout(resolve, 0))
          isUpdatingStore = false
        }
      }
    } catch (error) {
      console.error('[Indicators] Error creating TV study:', error)
    }
  }

  /**
   * Update an indicator in TradingView
   */
  function updateTVStudy(indicator: IndicatorInstance) {
    if (!isChartReady || !indicator.tv_study_id) return

    try {
      const chart = tvWidget.activeChart()
      if (!chart) return

      const study = chart.getStudyById(indicator.tv_study_id)
      if (!study) {
        console.warn('[Indicators] Study not found in TV, creating instead:', indicator.id)
        createTVStudy(indicator)
        return
      }

      const tvInputs = convertPandasTaParamsToTVInputs(indicator.pandas_ta_name, indicator.parameters)

      // Update study inputs
      chart.getStudyById(indicator.tv_study_id).applyOverrides(tvInputs)

      console.log('[Indicators] Updated study in TV:', indicator.id)
    } catch (error) {
      console.error('[Indicators] Error updating TV study:', error)
    }
  }

  /**
   * Remove an indicator from TradingView
   */
  function removeTVStudy(indicator: IndicatorInstance) {
    if (!isChartReady || !indicator.tv_study_id) return

    try {
      const chart = tvWidget.activeChart()
      if (!chart) return

      chart.removeStudy(indicator.tv_study_id)
      console.log('[Indicators] Removed study from TV:', indicator.id)
    } catch (error) {
      console.error('[Indicators] Error removing TV study:', error)
    }
  }

  // Initialize
  const cleanupIndicatorListeners = setupIndicatorListeners()
  setupStoreWatchers()

  // Return cleanup function
  return () => {
    cleanupIndicatorListeners()
  }
}
