<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, type WatchStopHandle } from 'vue'
import Card from 'primevue/card'
import { createTradingViewDatafeed } from '../composables/useTradingViewDatafeed'
import { useTradingViewShapes } from '../composables/useTradingViewShapes'
import { useTradingViewIndicators } from '../composables/useTradingViewIndicators'
import { useCustomIndicators, getCustomIndicatorsGetter } from '../composables/useCustomIndicators'
import { useChartStore } from '../stores/chart'
import { useIndicatorTypesStore } from '../stores/indicatorTypes'
import type { IChartingLibraryWidget } from '../types/tradingview'
import { intervalToSeconds } from '../utils'
import { wsManager } from '../composables/useWebSocket'

// Convert seconds to TradingView interval string.
// TradingView uses plain minute numbers ("60", "240") for intraday,
// and "1D", "2D" etc for daily. Never use "H" suffix — it's not in supported_resolutions.
function secondsToInterval(seconds: number): string {
  if (seconds % 86400 === 0) return `${seconds / 86400}D`
  return `${seconds / 60}` // plain number = minutes
}

const chartContainer = ref<HTMLDivElement | null>(null)
const chartStore = useChartStore()
let tvWidget: IChartingLibraryWidget | null = null
let datafeed: any = null
let isUpdatingFromChart = false // Flag to prevent circular updates
let shapeCleanup: (() => void) | null = null // Cleanup function for shape sync
let indicatorCleanup: (() => void) | null = null // Cleanup function for indicator sync
let customIndicatorCleanup: (() => void) | null = null // Cleanup for custom TV studies
let chartInitialized = false // Guard against double-init on reconnect
let symbolWatcher: WatchStopHandle | null = null

const maybeInitChart = () => {
  if (chartInitialized || !chartContainer.value) return
  // If the workspace persisted null values (bad state), reset to defaults immediately
  // rather than deferring forever. This also syncs the reset back to the container.
  if (!chartStore.symbol) chartStore.symbol = 'BTC/USDT.BINANCE'
  if (!chartStore.period) chartStore.period = 900
  chartInitialized = true
  initChart()
}

onMounted(() => {
  // Wait for workspace to be ready (persistent stores loaded from container)
  // before initializing TradingView, so stores are populated when onChartReady fires.
  watch(wsManager.sessionStatus, (status) => {
    if (status === 'ready') maybeInitChart()
  }, { immediate: true })
})

// Wait for TradingView library to load
function initChart() {
    if (!window.TradingView) {
      setTimeout(initChart, 100)
      return
    }

    try {
      datafeed = createTradingViewDatafeed()

      const indicatorTypesStore = useIndicatorTypesStore()
      console.log('[ChartView] indicator_types at widget init:', Object.keys(indicatorTypesStore.types))

      tvWidget = new window.TradingView.widget({
        symbol: chartStore.symbol, // Use symbol from store
        datafeed: datafeed,
        interval: secondsToInterval(chartStore.period || 900) as any,
        container: chartContainer.value!,
        library_path: '/charting_library/',
        locale: 'en',
        // Register named studies (one per custom indicator type) plus generic fallbacks.
        // Must be provided here — TV has no dynamic study registration API.
        // indicatorTypesStore is populated before widget creation (session ready guard).
        custom_indicators_getter: getCustomIndicatorsGetter(() => indicatorTypesStore.types),
        disabled_features: [
          'use_localstorage_for_settings',
          'header_symbol_search',
          'symbol_search_hot_key'
        ],
        enabled_features: [],
        fullscreen: false,
        autosize: true,
        theme: 'Dark',
        timezone: 'Etc/UTC'
      })

      tvWidget.onChartReady(() => {
        console.log('TradingView chart ready')
        setupChartListeners()
        setupStoreWatchers()
        // Initialize visible range on chart load
        initializeVisibleRange()
        // Setup shape synchronization
        if (tvWidget) {
          shapeCleanup = useTradingViewShapes(tvWidget)
          indicatorCleanup = useTradingViewIndicators(tvWidget)
          customIndicatorCleanup = useCustomIndicators(tvWidget)
        }
      })
    } catch (error) {
      console.error('Failed to initialize TradingView widget:', error)
    }
}

function initializeVisibleRange() {
  if (!tvWidget) return

  const chart = tvWidget.activeChart()
  const timeScale = chart.getVisibleRange()

  if (timeScale) {
    const startTime = Math.floor(timeScale.from)
    const endTime = Math.floor(timeScale.to)

    console.log('[ChartView] Initial visible range:', {
      from: new Date(startTime * 1000).toISOString(),
      to: new Date(endTime * 1000).toISOString()
    })

    chartStore.start_time = startTime
    chartStore.end_time = endTime
  }
}

function setupChartListeners() {
  if (!tvWidget) return

  const chart = tvWidget.activeChart()

  // Listen for symbol changes
  chart.onSymbolChanged().subscribe(null, () => {
    const symbolInfo = chart.symbolExt()
    if (symbolInfo && symbolInfo.ticker) {
      console.log('[ChartView] Symbol changed to:', symbolInfo.ticker)
      isUpdatingFromChart = true
      chartStore.symbol = symbolInfo.ticker
      isUpdatingFromChart = false
    }
  })

  // Listen for period changes
  chart.onIntervalChanged().subscribe(null, (interval: string) => {
    const seconds = intervalToSeconds(interval)
    console.log('[ChartView] Period changed to:', interval, `(${seconds}s)`)
    isUpdatingFromChart = true
    chartStore.period = seconds
    isUpdatingFromChart = false
  })

  // Listen for visible range changes (when user pans/zooms)
  chart.onVisibleRangeChanged().subscribe(null, () => {
    const timeScale = chart.getVisibleRange()
    if (timeScale) {
      // Convert from seconds to seconds (TradingView uses seconds for visible range)
      const startTime = Math.floor(timeScale.from)
      const endTime = Math.floor(timeScale.to)

      console.log('[ChartView] Visible range changed:', {
        from: new Date(startTime * 1000).toISOString(),
        to: new Date(endTime * 1000).toISOString()
      })

      isUpdatingFromChart = true
      chartStore.start_time = startTime
      chartStore.end_time = endTime
      isUpdatingFromChart = false
    }
  })
}

function setupStoreWatchers() {
  if (!tvWidget) return

  const chart = tvWidget.activeChart()

  // Watch for external changes to symbol (e.g., from backend/agent)
  watch(
    () => chartStore.symbol,
    (newSymbol) => {
      if (isUpdatingFromChart) return // Ignore updates that came from the chart itself

      console.log('[ChartView] Store symbol changed externally to:', newSymbol)
      const currentSymbol = chart.symbolExt()
      if (currentSymbol && currentSymbol.ticker !== newSymbol) {
        chart.setSymbol(newSymbol, () => {
          console.log('[ChartView] Chart symbol updated to:', newSymbol)
        })
      }
    }
  )

  // Watch for external changes to period
  watch(
    () => chartStore.period,
    (newPeriod) => {
      if (isUpdatingFromChart) return

      const tvInterval = secondsToInterval(newPeriod)
      console.log('[ChartView] Store period changed externally to:', newPeriod, `-> ${tvInterval}`)
      if (chart.resolution() !== tvInterval) {
        chart.setResolution(tvInterval, () => {
          console.log('[ChartView] Chart period updated to:', tvInterval)
        })
      }
    }
  )

  // Note: Visible range is typically only set by user interaction,
  // but we could add a watcher here if we want the backend to be able
  // to change the visible range programmatically
}

onBeforeUnmount(() => {
  if (symbolWatcher) {
    symbolWatcher()
    symbolWatcher = null
  }
  // Cleanup shape synchronization
  if (shapeCleanup) {
    shapeCleanup()
    shapeCleanup = null
  }

  // Cleanup indicator synchronization
  if (indicatorCleanup) {
    indicatorCleanup()
    indicatorCleanup = null
  }

  // Cleanup custom TV studies
  if (customIndicatorCleanup) {
    customIndicatorCleanup()
    customIndicatorCleanup = null
  }

  if (tvWidget) {
    tvWidget.remove()
    tvWidget = null
  }
  if (datafeed && typeof datafeed.destroy === 'function') {
    datafeed.destroy()
    datafeed = null
  }
})
</script>

<template>
  <Card class="chart-card">
    <template #content>
      <div ref="chartContainer" class="chart-container"></div>
    </template>
  </Card>
</template>

<style scoped>
.chart-card {
  height: 100% !important;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: none;
  border-radius: 0 !important;
  background: var(--p-surface-0);
}

.chart-card :deep(.p-card) {
  border-radius: 0 !important;
}

.chart-card :deep(.p-card-body) {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}

.chart-card :deep(.p-card-content) {
  flex: 1;
  display: flex;
  overflow: hidden;
  padding: 0;
}

.chart-container {
  flex: 1;
  width: 100%;
  height: 100%;
}
</style>
