<template>
  <div class="lora-randomizer-widget" @wheel="onWheel">
    <LoraRandomizerSettingsView
      :count-mode="state.countMode.value"
      :count-fixed="state.countFixed.value"
      :count-min="state.countMin.value"
      :count-max="state.countMax.value"
      :model-strength-min="state.modelStrengthMin.value"
      :model-strength-max="state.modelStrengthMax.value"
      :use-custom-clip-range="state.useCustomClipRange.value"
      :clip-strength-min="state.clipStrengthMin.value"
      :clip-strength-max="state.clipStrengthMax.value"
      :roll-mode="state.rollMode.value"
      :is-rolling="state.isRolling.value"
      :is-clip-strength-disabled="state.isClipStrengthDisabled.value"
      :last-used="state.lastUsed.value"
      :current-loras="currentLoras"
      :can-reuse-last="canReuseLast"
      :use-recommended-strength="state.useRecommendedStrength.value"
      :recommended-strength-scale-min="state.recommendedStrengthScaleMin.value"
      :recommended-strength-scale-max="state.recommendedStrengthScaleMax.value"
      @update:count-mode="state.countMode.value = $event"
      @update:count-fixed="state.countFixed.value = $event"
      @update:count-min="state.countMin.value = $event"
      @update:count-max="state.countMax.value = $event"
      @update:model-strength-min="state.modelStrengthMin.value = $event"
      @update:model-strength-max="state.modelStrengthMax.value = $event"
      @update:use-custom-clip-range="state.useCustomClipRange.value = $event"
      @update:clip-strength-min="state.clipStrengthMin.value = $event"
      @update:clip-strength-max="state.clipStrengthMax.value = $event"
      @update:roll-mode="state.rollMode.value = $event"
      @update:use-recommended-strength="state.useRecommendedStrength.value = $event"
      @update:recommended-strength-scale-min="state.recommendedStrengthScaleMin.value = $event"
      @update:recommended-strength-scale-max="state.recommendedStrengthScaleMax.value = $event"
      @generate-fixed="handleGenerateFixed"
      @always-randomize="handleAlwaysRandomize"
      @reuse-last="handleReuseLast"
    />
  </div>
</template>

<script setup lang="ts">
import { onMounted, computed, ref, watch } from 'vue'
import LoraRandomizerSettingsView from './lora-randomizer/LoraRandomizerSettingsView.vue'
import { useLoraRandomizerState } from '../composables/useLoraRandomizerState'
import type { ComponentWidget, RandomizerConfig, LoraEntry } from '../composables/types'

type RandomizerWidget = ComponentWidget<RandomizerConfig>

// Props
const props = defineProps<{
  widget: RandomizerWidget
  node: { id: number; inputs?: any[]; widgets?: any[]; graph?: any }
  api?: any
}>()

// State management
const state = useLoraRandomizerState(props.widget)

// Symbol to track if the widget has been executed at least once
const HAS_EXECUTED = Symbol('HAS_EXECUTED')

// Track current loras from the loras widget
const currentLoras = ref<LoraEntry[]>([])

// Track if component is mounted to avoid early watch triggers
const isMounted = ref(false)

interface PendingExecution {
  loras?: LoraEntry[]
  lastUsed?: LoraEntry[] | null
}

const pendingExecutions: PendingExecution[] = []

// Computed property to check if we can reuse last
const canReuseLast = computed(() => {
  const lastUsed = state.lastUsed.value
  if (!lastUsed || lastUsed.length === 0) return false
  return !areLorasEqual(currentLoras.value, lastUsed)
})

// Helper function to compare two lora lists
const areLorasEqual = (a: LoraEntry[], b: LoraEntry[]): boolean => {
  if (a.length !== b.length) return false
  const sortedA = [...a].sort((x, y) => x.name.localeCompare(y.name))
  const sortedB = [...b].sort((x, y) => x.name.localeCompare(y.name))
  return sortedA.every((lora, i) =>
    lora.name === sortedB[i].name &&
    lora.strength === sortedB[i].strength &&
    lora.clipStrength === sortedB[i].clipStrength
  )
}

// Handle "Generate Fixed" button click
const handleGenerateFixed = async () => {
  try {
    // Get pool config from connected pool_config input
    const poolConfig = (props.node as any).getPoolConfig?.() || null

    // Get locked loras from the loras widget
    const lorasWidget = props.node.widgets?.find((w: any) => w.name === "loras")
    const lockedLoras: LoraEntry[] = (lorasWidget?.value || []).filter((lora: LoraEntry) => lora.locked === true)

    // Call API to get random loras
    const randomLoras = await state.rollLoras(poolConfig, lockedLoras)

    // Update the loras widget with the new selection
    if (lorasWidget) {
      lorasWidget.value = randomLoras
      currentLoras.value = randomLoras
    }

    // Set roll mode to fixed
    state.rollMode.value = 'fixed'
  } catch (error) {
    console.error('[LoraRandomizerWidget] Error generating fixed LoRAs:', error)
    alert('Failed to generate LoRAs: ' + (error as Error).message)
  }
}

// Handle "Always Randomize" button click
const handleAlwaysRandomize = async () => {
  try {
    // Get pool config from connected pool_config input
    const poolConfig = (props.node as any).getPoolConfig?.() || null

    // Get locked loras from the loras widget
    const lorasWidget = props.node.widgets?.find((w: any) => w.name === "loras")
    const lockedLoras: LoraEntry[] = (lorasWidget?.value || []).filter((lora: LoraEntry) => lora.locked === true)

    // Call API to get random loras
    const randomLoras = await state.rollLoras(poolConfig, lockedLoras)

    // Update the loras widget with the new selection
    if (lorasWidget) {
      lorasWidget.value = randomLoras
      currentLoras.value = randomLoras
    }

    // Set roll mode to always
    state.rollMode.value = 'always'
  } catch (error) {
    console.error('[LoraRandomizerWidget] Error generating random LoRAs:', error)
    alert('Failed to generate LoRAs: ' + (error as Error).message)
  }
}

// Handle "Reuse Last" button click
const handleReuseLast = () => {
  const lastUsedLoras = state.useLastUsed()
  if (lastUsedLoras) {
    // Update the loras widget with the last used combination
    const lorasWidget = props.node.widgets?.find((w: any) => w.name === 'loras')
    if (lorasWidget) {
      lorasWidget.value = lastUsedLoras
      currentLoras.value = lastUsedLoras
    }

    // Switch to fixed mode
    state.rollMode.value = 'fixed'
  }
}

/**
 * Handle mouse wheel events on the widget.
 * Forwards the event to the ComfyUI canvas for zooming when appropriate.
 */
const onWheel = (event: WheelEvent) => {
  // Check if the event originated from a slider component
  // Sliders have data-capture-wheel="true" attribute
  const target = event.target as HTMLElement
  if (target?.closest('[data-capture-wheel="true"]')) {
    // Event is from a slider, slider already handled it
    // Just stop propagation to prevent canvas zoom
    event.stopPropagation()
    return
  }

  // Access ComfyUI app from global window
  const app = (window as any).app
  if (!app || !app.canvas || typeof app.canvas.processMouseWheel !== 'function') {
    return
  }

  const deltaX = event.deltaX
  const deltaY = event.deltaY
  const isHorizontal = Math.abs(deltaX) > Math.abs(deltaY)

  // 1. Handle pinch-to-zoom (ctrlKey is true for pinch-to-zoom on most browsers)
  if (event.ctrlKey) {
    event.preventDefault()
    event.stopPropagation()
    app.canvas.processMouseWheel(event)
    return
  }

  // 2. Horizontal scroll: pass to canvas (widgets usually don't scroll horizontally)
  if (isHorizontal) {
    event.preventDefault()
    event.stopPropagation()
    app.canvas.processMouseWheel(event)
    return
  }

  // 3. Vertical scrolling: forward to canvas
  event.preventDefault()
  event.stopPropagation()
  app.canvas.processMouseWheel(event)
}

// Watch for changes to the loras widget to track current loras
watch(() => props.node.widgets?.find((w: any) => w.name === 'loras')?.value, (newVal) => {
  // Only update after component is mounted
  if (isMounted.value) {
    if (newVal && Array.isArray(newVal)) {
      currentLoras.value = newVal
    }
  }
}, { immediate: true, deep: true })

// Lifecycle
onMounted(async () => {
  // IMPORTANT: Save the current loras widget value BEFORE setting isMounted to true
  // This prevents the watch from overwriting an empty value
  const lorasWidget = props.node.widgets?.find((w: any) => w.name === 'loras')
  if (lorasWidget) {
    const currentWidgetValue = lorasWidget.value
    if (currentWidgetValue && Array.isArray(currentWidgetValue) && currentWidgetValue.length > 0) {
      currentLoras.value = currentWidgetValue
    }
  }

  // Mark component as mounted so watch can now respond to changes
  isMounted.value = true

  // Setup callback for external value updates (e.g., workflow load, undo/redo)
  // ComfyUI calls this automatically after setValue() in domWidget.ts
  props.widget.callback = (v: RandomizerConfig) => {
    if (v) {
      state.restoreFromConfig(v)
    }
  }

  // Restore from saved value if workflow was already loaded
  if (props.widget.value) {
    state.restoreFromConfig(props.widget.value)
  }

  // Add beforeQueued hook to handle seed shifting for batch queue synchronization
  // This ensures each execution uses the loras that were displayed before that execution
  ;(props.widget as any).beforeQueued = () => {
    // Only process when roll_mode is 'always' (randomize on each execution)
    if (state.rollMode.value === 'always') {
      if ((props.widget as any)[HAS_EXECUTED]) {
        // After first execution: shift seeds (previous next_seed becomes execution_seed)
        state.generateNewSeed()
      } else {
        // First execution: just initialize next_seed (execution_seed stays null)
        // This means first execution uses loras from widget input
        state.initializeNextSeed()
        ;(props.widget as any)[HAS_EXECUTED] = true
      }

      // Update the widget value so the seeds are included in the serialized config
      props.widget.value = state.buildConfig()
    }
  }

  // Override onExecuted to handle backend UI updates
  const originalOnExecuted = (props.node as any).onExecuted?.bind(props.node)

  ;(props.node as any).onExecuted = function(output: any) {
    console.log("[LoraRandomizerWidget] Node executed with output:", output)

    const pendingUpdate: PendingExecution = {}

    if (output?.last_used !== undefined) {
      pendingUpdate.lastUsed = output.last_used
      console.log(`[LoraRandomizerWidget] Queued last_used update: ${output.last_used ? output.last_used.length : 0} LoRAs`)
    }

    if (output?.loras && Array.isArray(output.loras)) {
      pendingUpdate.loras = output.loras
      console.log("[LoraRandomizerWidget] Queued loras data from backend:", output.loras)
    }

    if (pendingUpdate.lastUsed !== undefined || pendingUpdate.loras !== undefined) {
      pendingExecutions.push(pendingUpdate)
    }

    // Call original onExecuted if it exists
    if (originalOnExecuted) {
      return originalOnExecuted(output)
    }
  }

  if (props.api) {
    const handleExecutionComplete = () => {
      if (pendingExecutions.length === 0) {
        return
      }

      const pending = pendingExecutions.shift()!

      if (pending.lastUsed !== undefined) {
        state.lastUsed.value = pending.lastUsed
      }

      if (pending.loras !== undefined) {
        const lorasWidget = props.node.widgets?.find((w: any) => w.name === 'loras')
        if (lorasWidget) {
          lorasWidget.value = pending.loras
        }
        currentLoras.value = pending.loras
      }
    }

    props.api.addEventListener('execution_success', handleExecutionComplete)
    props.api.addEventListener('execution_error', handleExecutionComplete)
    props.api.addEventListener('execution_interrupted', handleExecutionComplete)

    const apiCleanup = () => {
      props.api.removeEventListener('execution_success', handleExecutionComplete)
      props.api.removeEventListener('execution_error', handleExecutionComplete)
      props.api.removeEventListener('execution_interrupted', handleExecutionComplete)
    }

    const existingCleanup = (props.widget as any).onRemoveCleanup
    ;(props.widget as any).onRemoveCleanup = () => {
      existingCleanup?.()
      apiCleanup()
    }
  }
})
</script>

<style scoped>
.lora-randomizer-widget {
  padding: 6px;
  background: rgba(40, 44, 52, 0.6);
  border-radius: 6px;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-sizing: border-box;
}
</style>
