import { watch } from 'vue'
import type { IChartingLibraryWidget } from '../types/tradingview'
import { useShapeStore } from '../stores/shapes'
import { useChartStore } from '../stores/chart'
import type { Shape, ControlPoint } from '../stores/shapes'

/**
 * Map TradingView internal tool names (LineToolXXX) to TradingView's native shape type names
 * Used when converting user-drawn shapes to our Shape format
 */
const TV_TOOL_TO_SHAPE_TYPE: Record<string, string> = {
  'LineToolTrendLine': 'trend_line',
  'LineToolHorzLine': 'horizontal_line',
  'LineToolVertLine': 'vertical_line',
  'LineToolRectangle': 'rectangle',
  'LineToolFibRetracement': 'fib_retracement',
  'LineToolFibExtension': 'fib_trend_ext',
  'LineToolParallelChannel': 'parallel_channel',
  'LineToolArrow': 'arrow',
  'LineToolText': 'text',
  'LineToolCallout': 'text',
  'LineToolCircle': 'circle',
  'LineToolPath': 'path',
  'LineToolPitchfork': 'pitchfork',
  'LineToolGannFan': 'gannbox_fan',
  'LineToolHeadAndShoulders': 'head_and_shoulders'
}

/**
 * Map line styles between our format and TradingView format
 */
const LINE_STYLE_TO_TV: Record<string, number> = {
  'solid': 0,
  'dashed': 1,
  'dotted': 2
}

const TV_TO_LINE_STYLE: Record<number, string> = {
  0: 'solid',
  1: 'dashed',
  2: 'dotted'
}

/**
 * Convert TradingView shape to our Shape format
 */
function convertTVShapeToShape(tvShape: any, symbol: string, shapeId?: string, shapeName?: string): Shape {
  // Get shape ID - try multiple ways
  let id = shapeId || tvShape.id
  if (!id && typeof tvShape.getId === 'function') {
    try {
      id = tvShape.getId()
    } catch (e) {
      console.warn('[TradingView Shapes] Error calling getId:', e)
    }
  }

  // Get shape name/type - use provided name first, then try multiple properties
  if (!shapeName) {
    shapeName = tvShape.name
  }
  if (!shapeName && typeof tvShape.getName === 'function') {
    try {
      shapeName = tvShape.getName()
    } catch (e) {
      console.warn('[TradingView Shapes] Error calling getName:', e)
    }
  }

  // Fallback to checking shape type or tool name
  if (!shapeName) {
    shapeName = tvShape.toolName || tvShape.type || 'unknown'
  }

  const standardType = TV_TOOL_TO_SHAPE_TYPE[shapeName] || shapeName

  // Extract control points from TradingView shape
  const points: ControlPoint[] = []

  // Try multiple ways to get points from the shape
  let pointsData = tvShape.points || []

  // If getPoints method exists, use it
  if (typeof tvShape.getPoints === 'function') {
    try {
      pointsData = tvShape.getPoints() || []
    } catch (e) {
      console.warn('[TradingView Shapes] Error calling getPoints:', e)
    }
  }

  // Convert points to our format
  if (Array.isArray(pointsData)) {
    for (const point of pointsData) {
      if (point && typeof point === 'object') {
        const controlPoint: ControlPoint = {
          time: Math.floor(point.time),
          price: point.price
        }
        // Only include channel if it's actually set
        if (point.channel !== undefined && point.channel !== null) {
          controlPoint.channel = point.channel
        }
        points.push(controlPoint)
      }
    }
  }

  console.log(`[TradingView Shapes] Converting shape ${id} (${shapeName}), found ${points.length} points`, points)

  // Extract properties
  let properties = tvShape.properties || {}

  // If getProperties method exists, use it
  if (typeof tvShape.getProperties === 'function') {
    try {
      properties = tvShape.getProperties() || properties
    } catch (e) {
      console.warn('[TradingView Shapes] Error calling getProperties:', e)
    }
  }

  const color = properties.linecolor || '#2962FF'
  const lineWidth = properties.linewidth || 1
  const lineStyle = TV_TO_LINE_STYLE[properties.linestyle] || 'solid'

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

  return {
    id,
    type: standardType,
    points,
    color,
    line_width: lineWidth,
    line_style: lineStyle,
    properties: {
      ...properties,
      // Store original TradingView shape name for reference
      tv_shape_name: shapeName
    },
    symbol,
    created_at: now,
    modified_at: now
  }
}

/**
 * Convert interval string to seconds
 */
function intervalToSeconds(interval: string): number {
  // Handle plain numbers (TradingView uses integers for sub-hour intervals in minutes)
  const numericInterval = parseInt(interval)
  if (!isNaN(numericInterval) && interval === numericInterval.toString()) {
    return numericInterval * 60 // Plain number means minutes
  }

  // Handle formatted intervals like "15M", "1H", "1D"
  const match = interval.match(/^(\d+)([SMHDW])$/)
  if (!match) {
    console.warn('[TradingView Shapes] Unknown interval format:', interval)
    return 60 // Default to 1 minute
  }

  const value = parseInt(match[1])
  const unit = match[2]

  const multipliers: Record<string, number> = {
    'S': 1,
    'M': 60,
    'H': 3600,
    'D': 86400,
    'W': 604800
  }

  return value * (multipliers[unit] || 60)
}

/**
 * Canonicalize timestamp to candle boundary
 * TradingView requires timestamps to align exactly with candle start times
 */
function canonicalizeTimestamp(timestamp: number, intervalSeconds: number): number {
  // Round down to nearest candle boundary
  return Math.floor(timestamp / intervalSeconds) * intervalSeconds
}

/**
 * Setup bidirectional synchronization between TradingView shapes and ShapeStore
 */
export function useTradingViewShapes(tvWidget: IChartingLibraryWidget) {
  const shapeStore = useShapeStore()
  const chartStore = useChartStore()

  let isApplyingTVUpdate = false // Prevent circular updates when TV modifies shapes
  let isUpdatingStore = false // Prevent circular updates when we update the store from TV
  let isChartReady = false // Track if chart API is ready

  /**
   * Handle drawing events from TradingView
   */
  function handleDrawingEvent(shapeId: string, eventType: string) {
    if (isApplyingTVUpdate) return

    console.log('[TradingView Shapes] Drawing event:', eventType, shapeId)

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

      const currentSymbol = chartStore.chart_state.symbol

      if (eventType === 'remove') {
        isUpdatingStore = true
        shapeStore.removeShape(shapeId)
        isUpdatingStore = false
        return
      }

      // First get the shape name from getAllShapes (which has id and name)
      let shapeName: string | undefined
      const allShapes = chart.getAllShapes()
      if (allShapes) {
        const shapeInfo = allShapes.find((s: any) => s.id === shapeId)
        if (shapeInfo) {
          shapeName = shapeInfo.name
        }
      }

      // Then get the actual shape entity using getShapeById (which has methods)
      let tvShape
      try {
        tvShape = chart.getShapeById(shapeId)
      } catch (error) {
        console.warn('[TradingView Shapes] Could not get shape:', shapeId)
        return
      }

      if (!tvShape) return

      // Debug: log on first encounter
      if (eventType === 'create') {
        console.log('[TradingView Shapes] Shape entity methods:', Object.keys(tvShape))
        console.log('[TradingView Shapes] Shape name from getAllShapes:', shapeName)
      }

      if (eventType === 'create') {
        // Get points immediately - don't create shape without them
        const points = tvShape.getPoints()
        const controlPoints = points && points.length > 0 ? points.map(p => {
          const cp: ControlPoint = {
            time: Math.floor(p.time),
            price: p.price
          }
          if (p.channel !== undefined && p.channel !== null) {
            cp.channel = p.channel
          }
          return cp
        }) : []

        // Create shape with complete data
        const shape = convertTVShapeToShape(tvShape, currentSymbol, shapeId, shapeName)
        // Override points with the ones we just fetched
        shape.points = controlPoints

        console.log('[TradingView Shapes] Creating shape:', shapeId, shape.type, 'with', shape.points.length, 'points')
        isUpdatingStore = true
        shapeStore.addShape(shape)
        isUpdatingStore = false
      } else if (eventType === 'points_changed') {
        // Points updated - get latest points and create NEW array
        const points = tvShape.getPoints()
        if (points) {
          // Create a completely new array to ensure it's treated as a replacement
          const controlPoints = Array.from(points.map(p => {
            const cp: ControlPoint = {
              time: Math.floor(p.time),
              price: p.price
            }
            if (p.channel !== undefined && p.channel !== null) {
              cp.channel = p.channel
            }
            return cp
          }))
          console.log('[TradingView Shapes] Points changed:', shapeId, 'now has', controlPoints.length, 'points')
          isUpdatingStore = true
          shapeStore.updateShape(shapeId, { points: controlPoints })
          isUpdatingStore = false
        }
      } else if (eventType === 'properties_changed') {
        // Properties updated - get latest properties
        const properties = tvShape.getProperties()
        if (properties) {
          const color = properties.linecolor || '#2962FF'
          const lineWidth = properties.linewidth || 1
          const lineStyle = TV_TO_LINE_STYLE[properties.linestyle] || 'solid'
          console.log('[TradingView Shapes] Properties changed:', shapeId)
          isUpdatingStore = true
          shapeStore.updateShape(shapeId, {
            color,
            line_width: lineWidth,
            line_style: lineStyle,
            properties
          })
          isUpdatingStore = false
        }
      } else if (eventType === 'move') {
        // Shape moved - update points with NEW array
        const points = tvShape.getPoints()
        if (points) {
          const controlPoints = Array.from(points.map(p => {
            const cp: ControlPoint = {
              time: Math.floor(p.time),
              price: p.price
            }
            if (p.channel !== undefined && p.channel !== null) {
              cp.channel = p.channel
            }
            return cp
          }))
          console.log('[TradingView Shapes] Shape moved:', shapeId)
          isUpdatingStore = true
          shapeStore.updateShape(shapeId, { points: controlPoints })
          isUpdatingStore = false
        }
      }
    } catch (error) {
      console.error('[TradingView Shapes] Error handling drawing event:', error)
    }
  }

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

      console.log('[TradingView Shapes] Setting up drawing event subscriptions')

      // Subscribe to the drawing event which handles all shape events
      const unsubscribe = tvWidget.subscribe('drawing_event', (shapeId: string, eventType: string) => {
        handleDrawingEvent(shapeId, eventType)
      })

      // Initial sync on data load
      const dataLoadedSubscription = chart.onDataLoaded().subscribe(null, () => {
        console.log('[TradingView Shapes] Chart data loaded, performing initial sync')
        syncShapesFromTV()
      })

      // Cleanup function to unsubscribe
      return () => {
        console.log('[TradingView Shapes] Cleaning up event subscriptions')
        try {
          if (typeof unsubscribe === 'function') {
            unsubscribe()
          } else {
            console.warn('[TradingView Shapes] unsubscribe is not a function:', typeof unsubscribe)
          }

          if (dataLoadedSubscription && typeof dataLoadedSubscription.unsubscribe === 'function') {
            dataLoadedSubscription.unsubscribe()
          }
        } catch (error) {
          console.error('[TradingView Shapes] Error during cleanup:', error)
        }
      }
    } catch (error) {
      console.error('[TradingView Shapes] Error setting up shape listeners:', error)
      return () => {}
    }
  }

  /**
   * Scan TradingView for shapes and sync to store
   */
  function syncShapesFromTV() {
    if (!isChartReady) {
      return // Skip if chart isn't ready yet
    }

    try {
      const chart = tvWidget.activeChart()
      if (!chart) {
        return // Chart not available
      }

      const allShapes = chart.getAllShapes()

      if (!allShapes) return

      const currentSymbol = chartStore.chart_state.symbol

      // Track which shape IDs we've seen
      const seenIds = new Set<string>()

      isUpdatingStore = true
      try {
        for (const tvShape of allShapes) {
          seenIds.add(tvShape.id)

          const existingShape = shapeStore.getShape(tvShape.id)
          const newShape = convertTVShapeToShape(tvShape, currentSymbol)

          if (!existingShape) {
            // New shape - add it
            console.log('[TradingView Shapes] New shape detected:', tvShape.id, tvShape.name, 'with', newShape.points.length, 'points')
            shapeStore.addShape(newShape)
          } else {
            // Check if shape has changed
            const pointsChanged = JSON.stringify(existingShape.points) !== JSON.stringify(newShape.points)
            const hasChanged =
              pointsChanged ||
              existingShape.color !== newShape.color ||
              existingShape.line_width !== newShape.line_width ||
              existingShape.line_style !== newShape.line_style

            if (hasChanged) {
              if (pointsChanged) {
                console.log('[TradingView Shapes] Shape points changed:', tvShape.id,
                  'from', existingShape.points.length, 'to', newShape.points.length, 'points')
              } else {
                console.log('[TradingView Shapes] Shape properties modified:', tvShape.id)
              }
              shapeStore.updateShape(tvShape.id, {
                points: newShape.points,
                color: newShape.color,
                line_width: newShape.line_width,
                line_style: newShape.line_style,
                properties: newShape.properties
              })
            }
          }
        }

        // Check for deleted shapes
        const allStoreShapes = shapeStore.getAllShapes()
        for (const storeShape of allStoreShapes) {
          if (storeShape.symbol === currentSymbol && !seenIds.has(storeShape.id)) {
            console.log('[TradingView Shapes] Shape deleted:', storeShape.id)
            shapeStore.removeShape(storeShape.id)
          }
        }
      } finally {
        isUpdatingStore = false
      }

    } catch (error) {
      console.error('[TradingView Shapes] Error syncing shapes from TV:', error)
    }
  }

  /**
   * Setup watchers for ShapeStore changes to apply to TradingView
   */
  function setupStoreWatchers() {
    // Watch for shape store changes and apply to TradingView
    watch(
      () => shapeStore.shapes,
      async (newShapes, oldShapes) => {
        if (isUpdatingStore || !isChartReady) return

        isApplyingTVUpdate = true

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

          const currentSymbol = chartStore.chart_state.symbol

          // Find added shapes
          for (const [id, shape] of Object.entries(newShapes)) {
            // Only apply shapes for current symbol (or shapes with null symbol)
            if (shape.symbol && shape.symbol !== currentSymbol) continue

            if (!oldShapes || !oldShapes[id]) {
              // Shape was added to store
              await createTVShape(shape)
            } else {
              // Check if shape exists in TradingView
              let tvShape = null
              try {
                tvShape = chart.getShapeById(id)
              } catch (e) {
                // Shape doesn't exist in TV
              }

              if (!tvShape) {
                // Shape is in store but not in TradingView - create it
                await createTVShape(shape)
              } else if (JSON.stringify(shape) !== JSON.stringify(oldShapes[id])) {
                // Shape was modified
                updateTVShape(shape)
              }
            }
          }

          // Find removed shapes
          if (oldShapes) {
            for (const [id, shape] of Object.entries(oldShapes)) {
              if (shape.symbol && shape.symbol !== currentSymbol) continue

              if (!newShapes[id]) {
                removeTVShape(id)
              }
            }
          }
        } catch (error) {
          console.error('[TradingView Shapes] Error applying store changes:', error)
        } finally {
          isApplyingTVUpdate = false
        }
      },
      { deep: true }
    )
  }

  /**
   * Create a shape in TradingView
   */
  async function createTVShape(shape: Shape) {
    if (!isChartReady) return

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

      const currentSymbol = chartStore.chart_state.symbol

      // Get current chart interval and convert to seconds for timestamp canonicalization
      const interval = chartStore.chart_state.interval
      const intervalSeconds = intervalToSeconds(interval)

      // Convert points to TradingView format and canonicalize timestamps to candle boundaries
      const tvPoints = shape.points.map(p => {
        const canonicalTime = canonicalizeTimestamp(p.time, intervalSeconds)
        const point: any = {
          time: canonicalTime,
          price: p.price
        }
        // Only include channel if it has a value (for parallel channels, etc.)
        if (p.channel != null) {
          point.channel = p.channel
        }
        return point
      })

      // Build overrides - merge color/width/style with properties
      const overrides: any = {
        ...shape.properties
      }

      if (shape.color) {
        overrides.linecolor = shape.color
      }

      if (shape.line_width) {
        overrides.linewidth = shape.line_width
      }

      if (shape.line_style) {
        overrides.linestyle = LINE_STYLE_TO_TV[shape.line_style] || 0
      }

      const options = {
        shape: shape.type,
        zOrder: 'top',
        lock: false,
        disableSelection: false,
        disableSave: false,
        disableUndo: false,
        overrides
      }

      console.log(`[TradingView Shapes] Creating shape type="${shape.type}" with ${tvPoints.length} points:`, tvPoints)

      // Create the shape - use createShape for single points, createMultipointShape for multiple
      let entityIdPromise
      if (tvPoints.length === 1) {
        console.log(`[TradingView Shapes] Using createShape() for single point`)
        entityIdPromise = chart.createShape(tvPoints[0], options)
      } else {
        console.log(`[TradingView Shapes] Using createMultipointShape() for ${tvPoints.length} points`)
        entityIdPromise = chart.createMultipointShape(tvPoints, options)
      }

      const entityId = await entityIdPromise

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

        try {
          shapeStore.removeShape(shape.id)

          const updatedShape: Shape = {
            ...shape,
            id: entityId,
            original_id: shape.original_id || shape.id,
            symbol: shape.symbol || currentSymbol
          }

          shapeStore.addShape(updatedShape)
        } finally {
          await new Promise(resolve => setTimeout(resolve, 0))
          isUpdatingStore = false
        }
      }

      // Bring shape to front to ensure visibility
      try {
        const createdShape = chart.getShapeById(entityId)
        if (createdShape && typeof createdShape.bringToFront === 'function') {
          createdShape.bringToFront()
        }
      } catch (e) {
        // Ignore errors
      }

    } catch (error) {
      console.error('[TradingView Shapes] Error creating shape:', error)
    }
  }

  /**
   * Update a shape in TradingView
   */
  function updateTVShape(shape: Shape) {
    if (!isChartReady) return

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

      const tvShape = chart.getShapeById(shape.id)

      if (!tvShape) {
        console.warn('[TradingView Shapes] Shape not found in TV, creating instead:', shape.id)
        createTVShape(shape)
        return
      }

      // Update points
      const tvPoints = shape.points.map(p => {
        const point: { time: number; price: number; channel?: string } = {
          time: p.time,
          price: p.price
        }
        if (p.channel) {
          point.channel = p.channel
        }
        return point
      })
      tvShape.setPoints(tvPoints)

      // Update properties
      tvShape.setProperties({
        linecolor: shape.color || '#2962FF',
        linewidth: shape.line_width || 1,
        linestyle: LINE_STYLE_TO_TV[shape.line_style || 'solid'] || 0,
        ...shape.properties
      })

      console.log('[TradingView Shapes] Updated shape in TV:', shape.id)

    } catch (error) {
      console.error('[TradingView Shapes] Error updating shape in TV:', error)
    }
  }

  /**
   * Remove a shape from TradingView
   */
  function removeTVShape(shapeId: string) {
    if (!isChartReady) return

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

      const tvShape = chart.getShapeById(shapeId)

      if (tvShape) {
        chart.removeEntity(tvShape)
        console.log('[TradingView Shapes] Removed shape from TV:', shapeId)
      }

    } catch (error) {
      console.error('[TradingView Shapes] Error removing shape from TV:', error)
    }
  }

  /**
   * Setup selection tracking
   */
  function setupSelectionTracking() {
    if (!isChartReady) return

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

      chart.onSelectionChanged().subscribe(null, () => {
        const selectedObjects = chart.selection()

        // Filter for shape/drawing objects only
        const selectedShapeIds = selectedObjects
          .filter((obj: any) => obj.type === 'study' || obj.name?.startsWith('LineTool'))
          .map((obj: any) => obj.id)

        console.log('[TradingView Shapes] Selection changed:', selectedShapeIds)
        chartStore.chart_state.selected_shapes = selectedShapeIds
      })

    } catch (error) {
      console.warn('[TradingView Shapes] Selection tracking not available:', error)
    }
  }

  // Initialize
  const cleanupShapeListeners = setupShapeListeners()
  setupStoreWatchers()
  setupSelectionTracking()

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