import { render } from "hono/jsx/dom"
import { analyzeSprite, calculateCrop, type Crop } from "./analyze"

const STORAGE_KEY = "tiny-sprites-last-file"

const state = {
  spriteUrl: "",
  fileName: "",
  relativePath: "", // Path relative to assets dir for generated code
  imageWidth: 0,
  imageHeight: 0,
  frames: 4,
  frameDuration: 100,
  scale: 1,
  crop: undefined as Crop | undefined,
  availableImages: [] as string[],
}

// Load from localStorage
try {
  const stored = localStorage.getItem(STORAGE_KEY)
  if (stored) {
    const data = JSON.parse(stored)
    state.spriteUrl = data.spriteUrl || ""
    state.fileName = data.fileName || ""
    state.relativePath = data.relativePath || ""
    state.imageWidth = data.imageWidth || 0
    state.imageHeight = data.imageHeight || 0
    state.frames = data.frames || 4
    state.crop = data.crop
  }
} catch {}

const saveToStorage = () => {
  try {
    localStorage.setItem(
      STORAGE_KEY,
      JSON.stringify({
        spriteUrl: state.spriteUrl,
        fileName: state.fileName,
        relativePath: state.relativePath,
        imageWidth: state.imageWidth,
        imageHeight: state.imageHeight,
        frames: state.frames,
        crop: state.crop,
      })
    )
  } catch {}
}

const loadImageFromPath = (path: string) => {
  const url = `/assets/${encodeURIComponent(path).replace(/%2F/g, "/")}`
  const img = new Image()
  img.onload = () => {
    state.spriteUrl = url
    state.fileName = path.split("/").pop() || path
    state.relativePath = path
    state.imageWidth = img.width
    state.imageHeight = img.height

    try {
      const result = analyzeSprite(img)
      state.frames = result.frames
      state.crop = result.crop
      $<HTMLInputElement>("frames").value = String(result.frames)
      updateCropInputs()
      $("error-message").style.display = "none"
    } catch (e) {
      state.crop = undefined
      const msg = e instanceof Error ? e.message : "Unknown error"
      $("error-message").textContent = `Auto-detect failed: ${msg}. Set frame count manually.`
      $("error-message").style.display = "block"
    }

    saveToStorage()
    updatePreview()
    renderImagePicker($<HTMLInputElement>("filter").value.toLowerCase())
  }
  img.src = url
}

const $ = <T extends HTMLElement>(id: string) => document.getElementById(id) as T

const updateCropInputs = () => {
  $<HTMLInputElement>("crop-x").value = String(state.crop?.x ?? 0)
  $<HTMLInputElement>("crop-y").value = String(state.crop?.y ?? 0)
  $<HTMLInputElement>("crop-width").value = String(state.crop?.width ?? 0)
  $<HTMLInputElement>("crop-height").value = String(state.crop?.height ?? 0)
}

const updatePreview = () => {
  const { spriteUrl, imageWidth, imageHeight, frames, frameDuration, scale, crop } = state

  const frameWidth = imageWidth > 0 ? Math.floor(imageWidth / frames) : 32
  const frameHeight = imageHeight > 0 ? imageHeight : 32

  // Use crop dimensions if available, otherwise full frame
  const displayWidth = crop ? crop.width : frameWidth
  const displayHeight = crop ? crop.height : frameHeight
  const offsetX = crop ? crop.x : 0
  const offsetY = crop ? crop.y : 0

  const totalDuration = frames * frameDuration

  // Update info displays
  $("frame-info").textContent = spriteUrl ? `Frame: ${frameWidth} x ${frameHeight} px` : ""
  $("scale-label").textContent = `Scale (${scale}x)`

  // Update preview - show full frame with crop overlay
  const preview = $("preview")
  const previewStyle = $("preview-style")
  const cropOverlay = $("crop-overlay")

  if (spriteUrl) {
    const keyframeId = "sprite-preview"
    const fullKeyframes = `@keyframes ${keyframeId}{from{background-position:0 0}to{background-position:-${imageWidth * scale}px 0}}`
    previewStyle.textContent = fullKeyframes
    preview.style.width = `${frameWidth * scale}px`
    preview.style.height = `${frameHeight * scale}px`
    preview.style.backgroundImage = `url('${spriteUrl}')`
    preview.style.backgroundSize = `${imageWidth * scale}px ${imageHeight * scale}px`
    preview.style.animation = `${keyframeId} ${totalDuration}ms steps(${frames}) infinite`
    preview.style.display = "block"
    preview.style.position = "relative"

    // Position crop overlay to show what's kept vs cropped
    if (crop) {
      cropOverlay.style.position = "absolute"
      cropOverlay.style.top = `${offsetY * scale}px`
      cropOverlay.style.left = `${offsetX * scale}px`
      cropOverlay.style.width = `${displayWidth * scale}px`
      cropOverlay.style.height = `${displayHeight * scale}px`
      cropOverlay.style.boxShadow = `0 0 0 9999px rgba(0, 0, 0, 0.6)`
      cropOverlay.style.display = "block"
    } else {
      cropOverlay.style.display = "none"
    }

    $("preview-placeholder").style.display = "none"
  } else {
    preview.style.display = "none"
    cropOverlay.style.display = "none"
    $("preview-placeholder").style.display = "block"
  }

  // Update generated code
  const cropAttr = crop ? `\n  crop={{ x: ${crop.x}, y: ${crop.y}, width: ${crop.width}, height: ${crop.height} }}` : ""
  const scaleAttr = scale !== 1 ? `\n  scale={${scale}}` : ""
  const srcName = state.relativePath || state.fileName || "YOUR_SPRITE_URL"
  $<HTMLTextAreaElement>("code").value = `<Sprite
  src="${srcName}"
  frames={${frames}}
  frameDuration={${frameDuration}}
  sheetWidth={${imageWidth}}
  sheetHeight={${imageHeight}}${cropAttr}${scaleAttr}
/>`
}

const renderImagePicker = (filter = "") => {
  const container = $("image-picker")
  if (state.availableImages.length === 0) {
    container.style.display = "none"
    return
  }

  const filtered = filter
    ? state.availableImages.filter((p) => p.toLowerCase().includes(filter))
    : state.availableImages

  container.style.display = "block"
  container.innerHTML = ""

  const list = document.createElement("div")
  list.style.maxHeight = "300px"
  list.style.overflowY = "auto"
  list.style.background = "var(--pico-card-background-color)"
  list.style.borderRadius = "var(--pico-border-radius)"

  for (const path of filtered) {
    const item = document.createElement("div")
    item.style.cursor = "pointer"
    item.style.display = "flex"
    item.style.flexDirection = "column"
    item.style.alignItems = "center"
    item.style.gap = "4px"
    item.style.padding = "12px"
    item.style.borderBottom = "1px solid var(--pico-muted-border-color)"

    if (state.relativePath === path) {
      item.style.background = "var(--pico-primary-background)"
    }

    item.onmouseenter = () => {
      if (state.relativePath !== path) {
        item.style.background = "var(--pico-secondary-background)"
      }
    }
    item.onmouseleave = () => {
      if (state.relativePath !== path) {
        item.style.background = ""
      }
    }

    const img = document.createElement("img")
    img.src = `/assets/${encodeURIComponent(path).replace(/%2F/g, "/")}`
    img.style.maxWidth = "100%"
    img.style.height = "64px"
    img.style.objectFit = "contain"
    img.style.imageRendering = "pixelated"

    const label = document.createElement("div")
    label.style.fontSize = "11px"
    label.style.wordBreak = "break-all"
    label.style.textAlign = "center"
    label.textContent = path

    item.appendChild(img)
    item.appendChild(label)
    item.onclick = () => loadImageFromPath(path)

    list.appendChild(item)
  }

  container.appendChild(list)
}

const fetchImages = async () => {
  try {
    const res = await fetch("/api/images")
    const data = await res.json()
    state.availableImages = data.images || []
    renderImagePicker()
  } catch {
    state.availableImages = []
  }
}

const App = () => (
  <div class="grid">
    <div>
      <label>
        Filter
        <input
          type="text"
          id="filter"
          placeholder="Search images..."
          onInput={(e) => {
            const filter = (e.target as HTMLInputElement).value.toLowerCase()
            renderImagePicker(filter)
          }}
        />
      </label>
      <div id="image-picker" style={{ marginBottom: "1rem", display: "none" }}></div>
      <div
        id="error-message"
        style={{
          display: "none",
          padding: "0.5rem",
          marginBottom: "1rem",
          background: "var(--pico-del-color)",
          borderRadius: "var(--pico-border-radius)",
          color: "white",
        }}
      ></div>
      <label>
        Frame Count
        <input
          type="number"
          id="frames"
          value={state.frames}
          min={1}
          onInput={(e) => {
            state.frames = +(e.target as HTMLInputElement).value || 1
            // Recalculate crop when frames change
            if (state.spriteUrl) {
              const img = new Image()
              img.onload = () => {
                state.crop = calculateCrop(img, state.frames)
                updateCropInputs()
                saveToStorage()
                updatePreview()
              }
              img.src = state.spriteUrl
            } else {
              updatePreview()
            }
          }}
        />
      </label>
      <label>
        Frame Duration (ms)
        <input
          type="number"
          id="frameDuration"
          value={state.frameDuration}
          min={1}
          onInput={(e) => {
            state.frameDuration = +(e.target as HTMLInputElement).value || 100
            updatePreview()
          }}
        />
      </label>
      <label>
        <span id="scale-label">Scale (1x)</span>
        <input
          type="range"
          id="scale"
          value={state.scale}
          min={1}
          max={8}
          step={1}
          onInput={(e) => {
            state.scale = +(e.target as HTMLInputElement).value || 1
            updatePreview()
          }}
        />
      </label>
      <small id="frame-info"></small>
      <fieldset>
        <legend>Crop</legend>
        <div class="grid">
          <label>
            X
            <input
              type="number"
              id="crop-x"
              value={state.crop?.x ?? 0}
              min={0}
              onInput={(e) => {
                const val = +(e.target as HTMLInputElement).value || 0
                state.crop = { ...state.crop!, x: val }
                saveToStorage()
                updatePreview()
              }}
            />
          </label>
          <label>
            Y
            <input
              type="number"
              id="crop-y"
              value={state.crop?.y ?? 0}
              min={0}
              onInput={(e) => {
                const val = +(e.target as HTMLInputElement).value || 0
                state.crop = { ...state.crop!, y: val }
                saveToStorage()
                updatePreview()
              }}
            />
          </label>
        </div>
        <div class="grid">
          <label>
            Width
            <input
              type="number"
              id="crop-width"
              value={state.crop?.width ?? 0}
              min={1}
              onInput={(e) => {
                const val = +(e.target as HTMLInputElement).value || 1
                state.crop = { ...state.crop!, width: val }
                saveToStorage()
                updatePreview()
              }}
            />
          </label>
          <label>
            Height
            <input
              type="number"
              id="crop-height"
              value={state.crop?.height ?? 0}
              min={1}
              onInput={(e) => {
                const val = +(e.target as HTMLInputElement).value || 1
                state.crop = { ...state.crop!, height: val }
                saveToStorage()
                updatePreview()
              }}
            />
          </label>
        </div>
      </fieldset>
    </div>
    <div>
      <div class="preview-area" style={{ position: "relative", overflow: "hidden" }}>
        <style id="preview-style"></style>
        <div id="preview-container" style={{ position: "relative" }}>
          <div id="preview" style={{ display: "none" }}></div>
          <div id="crop-overlay" style={{ display: "none", pointerEvents: "none" }}></div>
        </div>
        <span id="preview-placeholder">Upload a spritesheet to preview</span>
      </div>
      <label>
        Generated Code
        <textarea id="code" readonly rows={10}></textarea>
      </label>
      <button onClick={() => navigator.clipboard.writeText($<HTMLTextAreaElement>("code").value)}>
        Copy Code
      </button>
    </div>
  </div>
)

render(<App />, document.getElementById("app")!)

// Initial update after render
setTimeout(() => {
  $<HTMLInputElement>("frames").value = String(state.frames)
  updateCropInputs()
  updatePreview()
  fetchImages()
}, 0)
