import type { ToolConfig } from './index'
import { useCanvasStore } from '../../../stores/canvas'
import { useWindowsStore } from '../../../stores/windows'
import {
  renderInlineComponent,
  type VueComponentDefinition
} from '../../dynamicComponents'

// ============================================
// SCRIPT LOGGER — records every canvas_js execution for snapshots
// ============================================
const scriptLog: string[] = []

export function getScriptLog(): string[] {
  return [...scriptLog]
}

export function clearScriptLog(): void {
  scriptLog.length = 0
}

// ============================================
// WINDOW DEFINITIONS — tracks component definition per rendered window
// ============================================
export interface WindowDefinitionEntry {
  source: 'inline' | 'db' | 'fs'
  componentId?: string
  definition: VueComponentDefinition
  componentProps: Record<string, any>
}

const windowDefinitions = new Map<string, WindowDefinitionEntry>()

export function getWindowDefinitions(): Map<string, WindowDefinitionEntry> {
  return windowDefinitions
}

export function clearWindowDefinitions(): void {
  windowDefinitions.clear()
}

// ============================================
// HELPERS
// ============================================

function getCanvasContainer() {
  return document.getElementById('canvas-content')
}

function removePlaceholder(container: HTMLElement) {
  const placeholder = container.querySelector('.canvas-placeholder')
  if (placeholder) {
    placeholder.remove()
    window.dispatchEvent(new CustomEvent('canvas-content-rendered'))
  }
}

function emitComponentRendered(args: any) {
  window.dispatchEvent(new CustomEvent('vue-component-rendered', {
    detail: {
      id: args.id,
      name: args.name,
      template: args.template,
      setup: args.setup,
      style: args.style,
      props: args.props,
      imports: args.imports
    }
  }))
}

export function createCanvasHandlers(): ToolConfig[] {
  return [
    {
      name: 'render_html',
      description: 'Renderiza HTML en el canvas. Soporta <script> y <style> tags.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          html: {
            type: 'string',
            description: 'El codigo HTML a renderizar'
          },
          mode: {
            type: 'string',
            enum: ['replace', 'append', 'prepend'],
            description: 'Modo: replace, append, prepend'
          }
        },
        required: ['html']
      },
      handler: (args: { html: string; mode?: string }) => {
        const container = getCanvasContainer()
        if (!container) return 'Error: canvas no encontrado'

        removePlaceholder(container)

        const mode = args.mode || 'replace'
        if (mode === 'replace') {
          container.innerHTML = args.html
        } else if (mode === 'append') {
          container.insertAdjacentHTML('beforeend', args.html)
        } else if (mode === 'prepend') {
          container.insertAdjacentHTML('afterbegin', args.html)
        }

        // Ejecutar scripts inline
        const scripts = container.querySelectorAll('script')
        scripts.forEach((oldScript) => {
          const newScript = document.createElement('script')
          Array.from(oldScript.attributes).forEach(attr => {
            newScript.setAttribute(attr.name, attr.value)
          })
          newScript.textContent = oldScript.textContent
          oldScript.parentNode?.replaceChild(newScript, oldScript)
        })

        const canvasStore = useCanvasStore()
        canvasStore.addToHistory({ tool: 'render_html', args, timestamp: Date.now() })
        return 'HTML renderizado'
      }
    },
    {
      name: 'render_vue_component',
      description: 'Renderiza un componente Vue 3 en una ventana flotante con drag, resize y close.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID unico del componente' },
          name: { type: 'string', description: 'Nombre del componente' },
          template: { type: 'string', description: 'Template HTML con sintaxis Vue' },
          setup: { type: 'string', description: 'Codigo de la funcion setup' },
          style: { type: 'string', description: 'CSS del componente' },
          props: { type: 'array', items: { type: 'string' }, description: 'Lista de props' },
          imports: { type: 'array', items: { type: 'string' }, description: 'Funciones de Vue a importar' },
          componentProps: { type: 'object', description: 'Valores para las props' },
          mode: { type: 'string', enum: ['replace', 'append'], description: 'Modo de renderizado' },
          x: { type: 'number', description: 'Posicion X inicial de la ventana' },
          y: { type: 'number', description: 'Posicion Y inicial de la ventana' },
          width: { type: 'number', description: 'Ancho inicial de la ventana' },
          height: { type: 'number', description: 'Alto inicial de la ventana' }
        },
        required: ['id', 'name', 'template']
      },
      handler: async (args: {
        id: string
        name: string
        template: string
        setup?: string
        style?: string
        props?: string[]
        imports?: string[]
        componentProps?: Record<string, any>
        mode?: string
        x?: number
        y?: number
        width?: number
        height?: number
      }) => {
        const container = getCanvasContainer()
        if (!container) return 'Error: canvas no encontrado'

        removePlaceholder(container)

        const definition: VueComponentDefinition = {
          id: args.id,
          name: args.name,
          template: args.template,
          setup: args.setup,
          style: args.style,
          props: args.props,
          imports: args.imports || ['ref', 'reactive', 'computed']
        }

        const isAppend = args.mode === 'append'
        const layout = {
          x: args.x,
          y: args.y,
          width: args.width,
          height: args.height
        }

        const result = renderInlineComponent(definition, container, args.componentProps || {}, isAppend, layout)

        // Track definition for snapshot capture
        windowDefinitions.set(args.id, {
          source: 'inline',
          definition,
          componentProps: args.componentProps || {}
        })

        ;(window as any).__vueComponentUnmount = result.unmount

        emitComponentRendered(args)

        const canvasStore = useCanvasStore()
        canvasStore.addToHistory({ tool: 'render_vue_component', args, timestamp: Date.now() })
        return `Componente Vue "${args.name}" renderizado en ventana flotante`
      }
    },
    {
      name: 'move_window',
      description: 'Mueve una ventana a una posicion especifica en el canvas.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID de la ventana a mover' },
          x: { type: 'number', description: 'Nueva posicion X' },
          y: { type: 'number', description: 'Nueva posicion Y' }
        },
        required: ['id', 'x', 'y']
      },
      handler: (args: { id: string; x: number; y: number }) => {
        const windowsStore = useWindowsStore()

        if (!windowsStore.has(args.id)) {
          return `Error: Ventana "${args.id}" no encontrada`
        }

        windowsStore.updatePosition(args.id, args.x, args.y)

        // Actualizar el DOM directamente
        const windowEl = document.querySelector(`[data-window-id="${args.id}"]`) as HTMLElement
        if (windowEl) {
          windowEl.style.left = `${args.x}px`
          windowEl.style.top = `${args.y}px`
        }

        return `Ventana "${args.id}" movida a (${args.x}, ${args.y})`
      }
    },
    {
      name: 'resize_window',
      description: 'Cambia el tamano de una ventana en el canvas.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID de la ventana a redimensionar' },
          width: { type: 'number', description: 'Nuevo ancho' },
          height: { type: 'number', description: 'Nuevo alto' }
        },
        required: ['id', 'width', 'height']
      },
      handler: (args: { id: string; width: number; height: number }) => {
        const windowsStore = useWindowsStore()

        if (!windowsStore.has(args.id)) {
          return `Error: Ventana "${args.id}" no encontrada`
        }

        windowsStore.updateSize(args.id, args.width, args.height)

        // Actualizar el DOM directamente
        const windowEl = document.querySelector(`[data-window-id="${args.id}"]`) as HTMLElement
        if (windowEl) {
          windowEl.style.width = `${args.width}px`
          windowEl.style.height = `${args.height}px`
        }

        return `Ventana "${args.id}" redimensionada a ${args.width}x${args.height}`
      }
    },
    {
      name: 'close_window',
      description: 'Cierra una ventana del canvas.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID de la ventana a cerrar' }
        },
        required: ['id']
      },
      handler: (args: { id: string }) => {
        const windowsStore = useWindowsStore()

        if (!windowsStore.has(args.id)) {
          return `Error: Ventana "${args.id}" no encontrada`
        }

        // Buscar y eliminar el contenedor
        const container = document.getElementById(`inline-${args.id}`)
        if (container) {
          container.remove()
        }

        // Eliminar estilos
        document.getElementById(`style-${args.id}`)?.remove()

        // Eliminar del store
        windowsStore.remove(args.id)
        windowDefinitions.delete(args.id)

        return `Ventana "${args.id}" cerrada`
      }
    },
    {
      name: 'list_windows',
      description: 'Lista todas las ventanas abiertas en el canvas.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {},
        required: []
      },
      handler: () => {
        const windowsStore = useWindowsStore()
        const windows = windowsStore.windowsList

        if (windows.length === 0) {
          return 'No hay ventanas abiertas'
        }

        const list = windows.map(w =>
          `- ${w.id}: "${w.title}" en (${w.x}, ${w.y}) - ${w.width}x${w.height}`
        ).join('\n')

        return `Ventanas abiertas (${windows.length}):\n${list}`
      }
    },
    {
      name: 'inspect_window',
      description: 'Inspecciona el contenido HTML de una ventana. Usa selector para filtrar elementos internos.',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID de la ventana' },
          selector: { type: 'string', description: 'Selector CSS dentro de la ventana' },
          attribute: {
            type: 'string',
            enum: ['innerHTML', 'outerHTML', 'textContent', 'attributes'],
            description: 'Que retornar (default: innerHTML)'
          },
          limit: { type: 'number', description: 'Max caracteres (default: 2000)' }
        },
        required: ['id']
      },
      handler: (args: { id: string; selector?: string; attribute?: string; limit?: number }) => {
        const windowEl = document.querySelector(`[data-window-id="${args.id}"]`)
        if (!windowEl) {
          return `Error: window ${args.id} not found`
        }

        const content = windowEl.querySelector('.window-content')
        if (!content) {
          return `Error: no content in ${args.id}`
        }

        let target: Element | null = content
        if (args.selector) {
          target = content.querySelector(args.selector)
          if (!target) {
            return `Error: no match ${args.selector} in ${args.id}`
          }
        }

        const attr = args.attribute || 'innerHTML'
        const limit = args.limit || 2000
        let result: string

        if (attr === 'attributes') {
          const attrs: Record<string, string> = {}
          for (const a of Array.from(target.attributes)) {
            attrs[a.name] = a.value
          }
          result = JSON.stringify(attrs, null, 2)
        } else {
          result = (target as any)[attr] || ''
        }

        if (result.length > limit) {
          result = result.slice(0, limit) + `\n...(+${result.length - limit})`
        }

        return result
      }
    },
    {
      name: 'get_canvas',
      description: 'Lee elementos del canvas con selector CSS',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          selector: { type: 'string', description: 'Selector CSS' },
          attribute: {
            type: 'string',
            enum: ['innerHTML', 'outerHTML', 'textContent', 'attributes', 'style', 'classList'],
            description: 'Que retornar (default: innerHTML)'
          },
          all: { type: 'boolean', description: 'Retorna todos los matches (default: false)' },
          limit: { type: 'number', description: 'Max caracteres (default: 3000)' }
        },
        required: ['selector']
      },
      handler: (args: { selector: string; attribute?: string; all?: boolean; limit?: number }) => {
        const container = getCanvasContainer()
        if (!container) return 'Error: canvas no encontrado'

        const attr = args.attribute || 'innerHTML'
        const limit = args.limit || 3000

        const extractValue = (el: Element): string => {
          if (attr === 'attributes') {
            const attrs: Record<string, string> = {}
            for (const a of Array.from(el.attributes)) {
              attrs[a.name] = a.value
            }
            return JSON.stringify(attrs)
          } else if (attr === 'style') {
            return (el as HTMLElement).style.cssText
          } else if (attr === 'classList') {
            return Array.from(el.classList).join(' ')
          } else {
            return (el as any)[attr] || ''
          }
        }

        let result: string

        if (args.all) {
          const elements = container.querySelectorAll(args.selector)
          if (elements.length === 0) {
            return `No match: ${args.selector}`
          }
          const values = Array.from(elements).map((el, i) => `[${i}] ${extractValue(el)}`)
          result = `${elements.length} elements:\n${values.join('\n---\n')}`
        } else {
          const el = container.querySelector(args.selector)
          if (!el) {
            return `No match: ${args.selector}`
          }
          result = extractValue(el)
        }

        if (result.length > limit) {
          result = result.slice(0, limit) + `\n...(+${result.length - limit})`
        }

        return result
      }
    },
    {
      name: 'edit_canvas',
      description: 'Edita elementos del canvas (old_value -> new_value)',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          selector: { type: 'string', description: 'Selector CSS' },
          attribute: {
            type: 'string',
            enum: ['innerHTML', 'textContent', 'outerHTML', 'style', 'className'],
            description: 'Que atributo editar (default: innerHTML)'
          },
          old_value: { type: 'string', description: 'Si se omite, reemplaza todo' },
          new_value: { type: 'string', description: 'Nuevo valor' },
          all: { type: 'boolean', description: 'Edita todos los matches (default: false)' }
        },
        required: ['selector', 'new_value']
      },
      handler: (args: { selector: string; attribute?: string; old_value?: string; new_value: string; all?: boolean }) => {
        const container = getCanvasContainer()
        if (!container) return 'Error: canvas no encontrado'

        const attr = args.attribute || 'innerHTML'
        const allElements = Array.from(container.querySelectorAll(args.selector))

        if (allElements.length === 0) {
          return `Error: no match ${args.selector}`
        }

        // Find which elements contain old_value
        const matchingElements = args.old_value
          ? allElements.filter(el => {
              const current = attr === 'style' ? (el as HTMLElement).style.cssText : (el as any)[attr]
              return current && current.includes(args.old_value!)
            })
          : allElements

        if (args.old_value && matchingElements.length === 0) {
          return `Error: old_value not found`
        }

        if (!args.all && matchingElements.length > 1) {
          return `Error: ${matchingElements.length} matches found. Use all:true or narrow selector.`
        }

        const elements = args.all ? matchingElements : [matchingElements[0]]
        let editedCount = 0

        for (const el of elements) {
          const htmlEl = el as HTMLElement

          if (args.old_value) {
            const current = attr === 'style' ? htmlEl.style.cssText : (htmlEl as any)[attr]
            const newVal = current.replace(args.old_value, args.new_value)
            if (attr === 'style') {
              htmlEl.style.cssText = newVal
            } else {
              (htmlEl as any)[attr] = newVal
            }
          } else {
            if (attr === 'style') {
              htmlEl.style.cssText = args.new_value
            } else {
              (htmlEl as any)[attr] = args.new_value
            }
          }
          editedCount++
        }

        return `OK ${editedCount} edited`
      }
    },
    {
      name: 'canvas_css',
      description: 'Inyecta CSS en el canvas',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          css: { type: 'string', description: 'Codigo CSS' },
          id: { type: 'string', description: 'ID del bloque (default: auto)' },
          mode: {
            type: 'string',
            enum: ['inject', 'replace', 'remove'],
            description: 'inject (default), replace, remove'
          }
        },
        required: []
      },
      handler: (args: { css?: string; id?: string; mode?: string }) => {
        const mode = args.mode || 'inject'
        const styleId = args.id ? `canvas-css-${args.id}` : `canvas-css-${Date.now()}`

        if (mode === 'remove') {
          if (!args.id) return 'Error: id required for remove'
          const existing = document.getElementById(`canvas-css-${args.id}`)
          if (existing) {
            existing.remove()
            return `OK css:${args.id} removed`
          }
          return `Error: css:${args.id} not found`
        }

        if (!args.css) return 'Error: css required for inject/replace'

        let styleEl = args.id ? document.getElementById(`canvas-css-${args.id}`) : null

        if (mode === 'replace' && styleEl) {
          styleEl.textContent = args.css
          return `OK css:${args.id} replaced`
        }

        if (!styleEl) {
          styleEl = document.createElement('style')
          styleEl.id = styleId
          document.head.appendChild(styleEl)
        }

        styleEl.textContent = args.css
        return `OK css:${styleId.replace('canvas-css-', '')}`
      }
    },
    {
      name: 'canvas_js',
      description: 'Ejecuta JavaScript en el contexto del canvas',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          code: { type: 'string', description: 'Codigo JavaScript a ejecutar' },
          async: { type: 'boolean', description: 'Ejecutar como async (default: false)' }
        },
        required: ['code']
      },
      handler: async (args: { code: string; async?: boolean }) => {
        try {
          // Log script for snapshot capture
          scriptLog.push(args.code)

          const canvas = getCanvasContainer()
          const windowsStore = useWindowsStore()

          // Contexto disponible para el codigo
          const context = {
            canvas,
            windows: windowsStore.windowsList,
            getWindow: (id: string) => document.querySelector(`[data-window-id="${id}"]`),
            $: (selector: string) => canvas?.querySelector(selector),
            $$: (selector: string) => canvas?.querySelectorAll(selector)
          }

          const fn = args.async
            ? new Function('ctx', `return (async () => { with(ctx) { ${args.code} } })()`)
            : new Function('ctx', `with(ctx) { ${args.code} }`)

          const result = args.async ? await fn(context) : fn(context)

          return result !== undefined ? String(result) : 'Ejecutado'
        } catch (e: any) {
          return `Error: ${e.message}`
        }
      }
    },
    {
      name: 'get_canvas_css',
      description: 'Obtiene CSS inyectado',
      category: 'canvas',
      schema: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'ID del bloque (si se omite, lista todos)' }
        },
        required: []
      },
      handler: (args: { id?: string }) => {
        if (args.id) {
          const styleEl = document.getElementById(`canvas-css-${args.id}`)
          if (!styleEl) return `Error: css:${args.id} not found`
          return styleEl.textContent || '(empty)'
        }

        // Listar todos
        const styles = document.querySelectorAll('style[id^="canvas-css-"]')
        if (styles.length === 0) {
          return 'No CSS injected'
        }

        const list = Array.from(styles).map(s => {
          const id = s.id.replace('canvas-css-', '')
          const preview = (s.textContent || '').slice(0, 100)
          return `- ${id}: ${preview}${(s.textContent?.length || 0) > 100 ? '...' : ''}`
        }).join('\n')

        return `${styles.length} blocks:\n${list}`
      }
    }
  ]
}
