import { Input, Label, Textarea, Checkbox, Button } from "@medusajs/ui"
import { useState, useRef, createContext, useContext, useEffect } from "react"
import { sdk } from "../lib/sdk"
import { toAbsoluteMediaUrl } from "../lib/media-url"
import { RichTextEditor } from "./RichTextEditor"
import { normalizeValue } from "../lib/cms-utils"

type ImageUploadContextValue = {
  uploadingFieldId: string | null
  setUploadingFieldId: (id: string | null) => void
}
const ImageUploadContext = createContext<ImageUploadContextValue | null>(null)

export function ImageUploadProvider({ children }: { children: React.ReactNode }) {
  const [uploadingFieldId, setUploadingFieldId] = useState<string | null>(null)
  return (
    <ImageUploadContext.Provider value={{ uploadingFieldId, setUploadingFieldId }}>
      {children}
    </ImageUploadContext.Provider>
  )
}

let uploadTail: Promise<unknown> = Promise.resolve()
const UPLOAD_DELAY_MS = 1200

function uploadOne(dataUrl: string, filename: string): Promise<{ url: string }> {
  const uploadPromise = uploadTail.then(() => doUpload(dataUrl, filename))
  uploadTail = uploadPromise.then(() => new Promise((r) => setTimeout(r, UPLOAD_DELAY_MS)))
  return uploadPromise
}

async function doUpload(dataUrl: string, filename: string, retry = true): Promise<{ url: string }> {
  const body = JSON.stringify({ file: dataUrl, filename })
  let r: Response
  try {
    r = await sdk.fetch("/admin/upload", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body,
      credentials: "include",
    })
  } catch (e) {
    const err = e as Error
    if (err?.message?.toLowerCase().includes("fetch") || err?.name === "TypeError") {
      throw new Error("Network error. Try a smaller image, check your connection, or try again.")
    }
    throw e
  }
  const text = await r.text()
  if (r.status === 401 && retry) {
    await new Promise((resolve) => setTimeout(resolve, 800))
    return doUpload(dataUrl, filename, false)
  }
  if (!r.ok) {
    let msg = text
    try { const j = JSON.parse(text); if (j.message) msg = j.message } catch { /* ignore */ }
    if (r.status === 401) {
      msg = "Unauthorized. Please refresh the page and try uploading again."
    }
    throw new Error(msg)
  }
  return JSON.parse(text) as { url: string }
}

function ImagePreview({ url, onRemove }: { url: string; onRemove: () => void }) {
  const [size, setSize] = useState<{ w: number; h: number } | null>(null)
  const previewUrl = toAbsoluteMediaUrl(url)
  return (
    <div className="relative border rounded overflow-hidden bg-ui-bg-subtle inline-block max-w-[280px]">
      <img
        src={previewUrl}
        alt=""
        className="block max-h-[200px] w-auto h-auto object-contain"
        style={{ maxWidth: "280px" }}
        onLoad={(e) => {
          const img = e.currentTarget
          if (img.naturalWidth) setSize({ w: img.naturalWidth, h: img.naturalHeight })
        }}
      />
      {size && (
        <div className="text-xs text-ui-fg-muted px-2 py-1 bg-ui-bg-subtle border-t border-ui-border-base">
          {size.w} × {size.h}
        </div>
      )}
      <button type="button" className="absolute top-0 right-0 bg-red-500 text-white text-xs px-1.5 py-0.5 rounded-bl" onClick={(e) => { e.preventDefault(); onRemove(); }}>×</button>
    </div>
  )
}

type Field = {
  id: string
  api_key: string
  label: string
  field_type: string
  required?: boolean
  default_value?: unknown
  validation_rules?: Record<string, unknown>
}

type Props = {
  field: Field
  value: unknown
  onChange: (v: unknown) => void
  activeLocale: string
}

const CMS_LOCALE_STORAGE_KEY = "biomket.cms.selected-locale"
const DEFAULT_LOCALE_OPTION = "__default__"

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value)
}

function hasLocalizedShape(value: unknown): value is Record<string, unknown> {
  if (!isRecord(value)) return false
  if ("default" in value) return true
  return Object.keys(value).some((key) => /^[a-z]{2}-[a-zA-Z]{2}$/.test(key))
}

function resolveLocalizedValue(raw: unknown, activeLocale: string): unknown {
  if (!hasLocalizedShape(raw)) {
    // In non-default locale mode, plain (non-localized) values are treated as
    // "missing translation" and should render blank in the editor.
    if (activeLocale && activeLocale !== DEFAULT_LOCALE_OPTION) {
      return ""
    }
    return raw
  }
  const record = raw as Record<string, unknown>
  if (!activeLocale || activeLocale === DEFAULT_LOCALE_OPTION) {
    return record.default
  }
  if (record[activeLocale] !== undefined) return record[activeLocale]
  const languageCode = activeLocale.split("-")[0]
  if (languageCode && record[languageCode] !== undefined) return record[languageCode]
  // Explicit locale mode: when missing translation, return blank instead of default.
  return ""
}

export function DynamicFieldRenderer({ field, value, onChange, activeLocale }: Props) {
  const [uploading, setUploading] = useState(false)
  const [uploadError, setUploadError] = useState<string | null>(null)
  const inputRef = useRef<HTMLInputElement>(null)

  const rawValue = value ?? field.default_value
  const localizedValue = resolveLocalizedValue(rawValue, activeLocale)
  const v =
    (typeof localizedValue === "string" ? localizedValue : "") ||
    (field.field_type === "number"
      ? 0
      : field.field_type === "boolean"
        ? false
        : field.field_type === "image"
          ? []
          : "")

  const emitChange = (next: unknown) => {
    if (!activeLocale || activeLocale === DEFAULT_LOCALE_OPTION) {
      if (hasLocalizedShape(value)) {
        onChange({
          ...(value as Record<string, unknown>),
          default: next,
        })
      } else {
        onChange(next)
      }
      return
    }

    const baseDefault =
      hasLocalizedShape(value) && (value as Record<string, unknown>).default !== undefined
        ? (value as Record<string, unknown>).default
        : value ?? field.default_value ?? ""

    const nextLocalized = (hasLocalizedShape(value) ? { ...(value as Record<string, unknown>) } : { default: baseDefault }) as Record<string, unknown>
    nextLocalized[activeLocale] = next
    onChange(nextLocalized)
  }

  const isRichtext = (field.field_type || "").toLowerCase().replace(/\s+/g, "") === "richtext"

  if (isRichtext) {
    const htmlValue = (typeof v === "string" ? v : (v != null ? String(v) : "")) || ""
    return (
      <div key={field.id}>
        <RichTextEditor
          key={field.id}
          value={htmlValue}
          onChange={(html) => emitChange(html)}
          placeholder={`Enter ${field.label.toLowerCase()}`}
          label={field.label}
          required={field.required}
        />
      </div>
    )
  }

  if (field.field_type === "textarea") {
    return (
      <div>
        <Label>{field.label}{field.required ? " *" : ""}</Label>
        <Textarea value={String(v)} onChange={(e) => emitChange(e.target.value)} rows={4} placeholder={`Enter ${field.label.toLowerCase()}`} />
      </div>
    )
  }
  if (field.field_type === "number") {
    const numberValue = v === "" || v === null || v === undefined ? "" : Number(v)
    return (
      <div>
        <Label>{field.label}{field.required ? " *" : ""}</Label>
        <Input type="number" value={numberValue} onChange={(e) => emitChange(e.target.value === "" ? "" : Number(e.target.value))} placeholder="0" />
      </div>
    )
  }
  if (field.field_type === "boolean") {
    return (
      <div className="flex items-center gap-2">
        <Checkbox checked={Boolean(v)} onCheckedChange={(c) => emitChange(!!c)} />
        <Label>{field.label}</Label>
      </div>
    )
  }
  if (field.field_type === "url" || field.field_type === "email") {
    return (
      <div>
        <Label>{field.label}{field.required ? " *" : ""}</Label>
        <Input type={field.field_type} value={String(v)} onChange={(e) => emitChange(e.target.value)} placeholder={field.field_type === "url" ? "https://…" : "email@example.com"} />
      </div>
    )
  }
  if (field.field_type === "json") {
    const raw = typeof v === "string" ? v : JSON.stringify(v ?? {}, null, 2)
    return (
      <div>
        <Label>{field.label}{field.required ? " *" : ""}</Label>
        <Textarea value={raw} onChange={(e) => { try { emitChange(JSON.parse(e.target.value || "{}")); } catch { emitChange(e.target.value); } }} rows={6} className="font-mono text-sm" placeholder="{}" />
      </div>
    )
  }
  if (field.field_type === "image") {
    const ctx = useContext(ImageUploadContext)
    const uploadingFieldId = ctx?.uploadingFieldId ?? null
    const setUploadingFieldId = ctx?.setUploadingFieldId ?? (() => {})
    const thisUploading = ctx ? uploadingFieldId === field.id : uploading
    const otherUploading = ctx ? (uploadingFieldId !== null && uploadingFieldId !== field.id) : false
    const urls = Array.isArray(v) ? (v.filter(Boolean) as string[]) : v ? [v as string] : []
    const handleFile = async (files: FileList | null) => {
      if (!files?.length) return
      setUploadError(null)
      setUploading(true)
      setUploadingFieldId(field.id)
      try {
        const file = files[0]
        const reader = new FileReader()
        let dataUrl = await new Promise<string>((resolve, reject) => {
          reader.onload = () => resolve(reader.result as string)
          reader.onerror = reject
          reader.readAsDataURL(file)
        })
        const data = await uploadOne(dataUrl, file.name)
        emitChange(data.url)
      } catch (e) {
        setUploadError((e as Error).message)
      } finally {
        setUploading(false)
        setUploadingFieldId(null)
        if (inputRef.current) inputRef.current.value = ""
      }
    }
    const remove = (_index: number) => {
      emitChange("")
    }
    return (
      <div>
        <Label>{field.label}{field.required ? " *" : ""}</Label>
        <input
          ref={inputRef}
          type="file"
          accept="image/*"
          className="hidden"
          onChange={(e) => handleFile(e.target.files)}
        />
        <div className="flex flex-wrap gap-2 items-center mt-1">
          <Button type="button" size="small" variant="secondary" disabled={thisUploading || otherUploading} onClick={() => inputRef.current?.click()}>
            {thisUploading ? "Uploading…" : otherUploading ? "Upload in progress…" : urls.length > 0 ? "Replace image" : "Upload image"}
          </Button>
          {uploadError && <span className="text-red-600 text-sm">{uploadError}</span>}
        </div>
        <p className="mt-1 text-ui-fg-muted text-xs">
          Stored at full size. Preview below is shown resized. Width × Height: any. Max file size ~50MB.
        </p>
        {urls.length > 0 ? (
          <div className="flex flex-wrap gap-3 mt-3">
            {urls.map((url, i) => (
              <ImagePreview key={i} url={url} onRemove={() => remove(i)} />
            ))}
          </div>
        ) : (
          <div
            className={`mt-3 border border-dashed rounded-lg p-6 text-center text-sm transition-colors ${(thisUploading || otherUploading) ? "border-ui-border-base bg-ui-bg-subtle cursor-not-allowed opacity-70" : "border-ui-border-base text-ui-fg-muted cursor-pointer hover:border-ui-border-strong hover:bg-ui-bg-subtle"}`}
            onClick={() => !thisUploading && !otherUploading && inputRef.current?.click()}
            role="button"
            tabIndex={(thisUploading || otherUploading) ? -1 : 0}
            onKeyDown={(e) => !thisUploading && !otherUploading && e.key === "Enter" && inputRef.current?.click()}
          >
            <span className="block">{thisUploading ? "Uploading…" : urls.length > 0 ? "Replace image" : "Upload image"}</span>
            <span className="block mt-1 opacity-80">Stored at actual size. Preview shown resized. Any dimensions, up to ~50MB.</span>
            <span className="block mt-0.5 opacity-70">Click or use the button above</span>
          </div>
        )}
      </div>
    )
  }
  return (
    <div>
      <Label>{field.label}{field.required ? " *" : ""}</Label>
      <Input value={String(v)} onChange={(e) => emitChange(e.target.value)} placeholder={`Enter ${field.label.toLowerCase()}`} />
    </div>
  )
}
