import { BaseBoxShapeUtil, TLBaseShape, HTMLContainer } from "tldraw"
import { useCallback, useState } from "react"
import { StandardizedToolWrapper } from "../components/StandardizedToolWrapper"
import { usePinnedToView } from "../hooks/usePinnedToView"
import { useMaximize } from "../hooks/useMaximize"

export type IEmbedShape = TLBaseShape<
  "Embed",
  {
    w: number
    h: number
    url: string | null
    pinnedToView: boolean
    tags: string[]
    interactionState?: {
      scrollPosition?: { x: number; y: number }
      currentTime?: number
    }
  }
>

const transformUrl = (url: string): string => {
  // YouTube
  const youtubeMatch = url.match(
    /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/,
  )
  if (youtubeMatch) {
    return `https://www.youtube.com/embed/${youtubeMatch[1]}`
  }

  // OpenStreetMap (handles google.com/maps URLs too — converts to OSM)
  if (url.includes("google.com/maps") || url.includes("goo.gl/maps") ||
      url.includes("openstreetmap.org")) {
    if (url.includes("openstreetmap.org/export/embed")) {
      return url
    }

    // Extract coordinates from Google Maps URL
    const coordMatch = url.match(/@(-?\d+\.?\d*),(-?\d+\.?\d*),?(\d+)?z?/)
    if (coordMatch) {
      const [, lat, lon, zoom] = coordMatch
      const z = zoom || '15'
      return `https://www.openstreetmap.org/export/embed.html?bbox=${Number(lon)-0.01},${Number(lat)-0.01},${Number(lon)+0.01},${Number(lat)+0.01}&layer=mapnik&marker=${lat},${lon}`
    }

    // Extract search query and embed via OSM
    const qMatch = url.match(/[?&]q=([^&]+)/) || url.match(/place\/([^\/]+)/)
    if (qMatch) {
      const query = decodeURIComponent(qMatch[1].replace(/\+/g, ' '))
      return `https://www.openstreetmap.org/export/embed.html?bbox=-180,-90,180,90&layer=mapnik`
    }

    // Fallback: OSM world view
    return `https://www.openstreetmap.org/export/embed.html?bbox=-180,-90,180,90&layer=mapnik`
  }

  // Twitter/X
  const xMatch = url.match(
    /(?:twitter\.com|x\.com)\/([^\/\s?]+)(?:\/(?:status|tweets)\/(\d+)|$)/,
  )
  if (xMatch) {
    const [, username, tweetId] = xMatch
    if (tweetId) {
      return `https://platform.x.com/embed/Tweet.html?id=${tweetId}`
    } else {
      return "about:blank"
    }
  }

  // Medium - return about:blank
  if (url.includes("medium.com")) {
    return "about:blank"
  }

  // Gather.town
  if (url.includes("app.gather.town")) {
    return url.replace("app.gather.town", "gather.town/embed")
  }

  return url
}

const getDefaultDimensions = (url: string): { w: number; h: number } => {
  if (url.match(/(?:youtube\.com|youtu\.be)/)) {
    return { w: 800, h: 450 }
  }

  if (url.match(/(?:twitter\.com|x\.com)/)) {
    if (url.match(/\/status\/|\/tweets\//)) {
      return { w: 800, h: 600 }
    }
  }

  if (url.includes("google.com/maps") || url.includes("goo.gl/maps") || url.includes("openstreetmap.org")) {
    return { w: 800, h: 600 }
  }

  if (url.includes("gather.town")) {
    return { w: 800, h: 600 }
  }

  return { w: 800, h: 600 }
}

const getFaviconUrl = (url: string): string => {
  try {
    const urlObj = new URL(url)
    return `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=32`
  } catch {
    return ''
  }
}

const getDisplayTitle = (url: string): string => {
  try {
    const urlObj = new URL(url)
    if (urlObj.hostname.includes('youtube.com')) {
      return 'YouTube'
    }
    if (urlObj.hostname.includes('twitter.com') || urlObj.hostname.includes('x.com')) {
      return 'Twitter/X'
    }
    if (urlObj.hostname.includes('google.com/maps') || urlObj.hostname.includes('openstreetmap.org')) {
      return 'Map'
    }
    return urlObj.hostname.replace('www.', '')
  } catch {
    return url
  }
}

export class EmbedShape extends BaseBoxShapeUtil<IEmbedShape> {
  static override type = "Embed"

  // Embed theme color: Yellow (Rainbow)
  static readonly PRIMARY_COLOR = "#eab308"

  getDefaultProps(): IEmbedShape["props"] {
    return {
      url: null,
      w: 800,
      h: 600,
      pinnedToView: false,
      tags: ['embed'],
    }
  }

  indicator(shape: IEmbedShape) {
    return (
      <rect
        x={0}
        y={0}
        width={shape.props.w}
        height={shape.props.h}
        fill="none"
      />
    )
  }

  component(shape: IEmbedShape) {
    const props = shape.props || {}
    const url = props.url || ""
    const [isMinimized, setIsMinimized] = useState(false)
    const isSelected = this.editor.getSelectedShapeIds().includes(shape.id)

    const [inputUrl, setInputUrl] = useState(url)
    const [error, setError] = useState("")

    // Use the pinning hook
    usePinnedToView(this.editor, shape.id, shape.props.pinnedToView)

    // Use the maximize hook for fullscreen functionality
    const { isMaximized, toggleMaximize } = useMaximize({
      editor: this.editor,
      shapeId: shape.id,
      currentW: shape.props.w,
      currentH: shape.props.h,
      shapeType: 'Embed',
    })

    const handleClose = () => {
      this.editor.deleteShape(shape.id)
    }

    const handleMinimize = () => {
      setIsMinimized(!isMinimized)
    }

    const handlePinToggle = () => {
      this.editor.updateShape<IEmbedShape>({
        id: shape.id,
        type: shape.type,
        props: {
          ...shape.props,
          pinnedToView: !shape.props.pinnedToView,
        },
      })
    }

    const handleSubmit = useCallback(
      (e: React.FormEvent) => {
        e.preventDefault()
        let completedUrl =
          inputUrl.startsWith("http://") || inputUrl.startsWith("https://")
            ? inputUrl
            : `https://${inputUrl}`

        const isValidUrl = completedUrl.match(/(^\w+:|^)\/\//)
        if (!isValidUrl) {
          setError("Invalid URL")
          return
        }

        this.editor.updateShape<IEmbedShape>({
          id: shape.id,
          type: "Embed",
          props: { ...shape.props, url: completedUrl },
        })
        setError("")
      },
      [inputUrl],
    )

    const handleIframeInteraction = (
      newState: typeof shape.props.interactionState,
    ) => {
      this.editor.updateShape<IEmbedShape>({
        id: shape.id,
        type: "Embed",
        props: {
          ...shape.props,
          interactionState: newState,
        },
      })
    }

    // Custom header content with URL info
    const headerContent = url ? (
      <div style={{ display: 'flex', alignItems: 'center', gap: '8px', flex: 1, overflow: 'hidden' }}>
        <img
          src={getFaviconUrl(url)}
          alt=""
          style={{
            width: "16px",
            height: "16px",
            flexShrink: 0,
          }}
          onError={(e) => {
            (e.target as HTMLImageElement).style.display = 'none'
          }}
        />
        <span style={{
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap',
          fontSize: '13px',
          fontWeight: 600
        }}>
          {getDisplayTitle(url)}
        </span>
      </div>
    ) : (
      <span>Embed</span>
    )

    // For empty state - URL input form
    if (!url) {
      return (
        <HTMLContainer style={{ width: shape.props.w, height: shape.props.h }}>
          <StandardizedToolWrapper
            title="Embed"
            primaryColor={EmbedShape.PRIMARY_COLOR}
            isSelected={isSelected}
            width={shape.props.w}
            height={shape.props.h}
            onClose={handleClose}
            onMinimize={handleMinimize}
            isMinimized={isMinimized}
            onMaximize={toggleMaximize}
            isMaximized={isMaximized}
            editor={this.editor}
            shapeId={shape.id}
            isPinnedToView={shape.props.pinnedToView}
            onPinToggle={handlePinToggle}
            tags={shape.props.tags}
            onTagsChange={(newTags) => {
              this.editor.updateShape<IEmbedShape>({
                id: shape.id,
                type: 'Embed',
                props: {
                  ...shape.props,
                  tags: newTags,
                }
              })
            }}
            tagsEditable={true}
          >
            <div
              style={{
                display: 'flex',
                flexDirection: 'column',
                justifyContent: 'center',
                alignItems: 'center',
                height: '100%',
                padding: '20px',
                cursor: 'text',
              }}
              onClick={(e) => {
                e.preventDefault()
                e.stopPropagation()
                const input = e.currentTarget.querySelector('input')
                input?.focus()
              }}
            >
              <form
                onSubmit={handleSubmit}
                style={{
                  width: "100%",
                  maxWidth: "500px",
                }}
                onClick={(e) => e.stopPropagation()}
              >
                <input
                  type="text"
                  value={inputUrl}
                  onChange={(e) => setInputUrl(e.target.value)}
                  placeholder="Enter URL to embed..."
                  style={{
                    width: "100%",
                    padding: "15px",
                    border: "1px solid #ccc",
                    borderRadius: "4px",
                    fontSize: "16px",
                    touchAction: 'manipulation',
                  }}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") {
                      handleSubmit(e)
                    }
                  }}
                  onPointerDown={(e) => {
                    e.stopPropagation()
                    e.currentTarget.focus()
                  }}
                />
                {error && (
                  <div style={{ color: "red", marginTop: "10px", textAlign: 'center' }}>{error}</div>
                )}
              </form>
            </div>
          </StandardizedToolWrapper>
        </HTMLContainer>
      )
    }

    // For medium.com and twitter profile views
    if (url.includes("medium.com") ||
        (url && url.match(/(?:twitter\.com|x\.com)\/[^\/]+$/))) {
      return (
        <HTMLContainer style={{ width: shape.props.w, height: shape.props.h }}>
          <StandardizedToolWrapper
            title="Embed"
            headerContent={headerContent}
            primaryColor={EmbedShape.PRIMARY_COLOR}
            isSelected={isSelected}
            width={shape.props.w}
            height={shape.props.h}
            onClose={handleClose}
            onMinimize={handleMinimize}
            isMinimized={isMinimized}
            onMaximize={toggleMaximize}
            isMaximized={isMaximized}
            editor={this.editor}
            shapeId={shape.id}
            isPinnedToView={shape.props.pinnedToView}
            onPinToggle={handlePinToggle}
            tags={shape.props.tags}
            onTagsChange={(newTags) => {
              this.editor.updateShape<IEmbedShape>({
                id: shape.id,
                type: 'Embed',
                props: {
                  ...shape.props,
                  tags: newTags,
                }
              })
            }}
            tagsEditable={true}
          >
            <div
              style={{
                display: 'flex',
                flexDirection: "column",
                gap: "12px",
                padding: "20px",
                textAlign: "center",
                height: '100%',
                justifyContent: 'center',
                alignItems: 'center',
              }}
            >
              <p>
                This content cannot be embedded in an iframe.
              </p>
              <button
                onClick={() => window.open(url, '_blank', 'noopener,noreferrer')}
                style={{
                  padding: '10px 20px',
                  backgroundColor: EmbedShape.PRIMARY_COLOR,
                  color: 'white',
                  border: 'none',
                  borderRadius: '6px',
                  cursor: 'pointer',
                  fontSize: '14px',
                  fontWeight: 500,
                  touchAction: 'manipulation',
                }}
                onPointerDown={(e) => e.stopPropagation()}
              >
                Open in new tab →
              </button>
            </div>
          </StandardizedToolWrapper>
        </HTMLContainer>
      )
    }

    // For normal embed view
    return (
      <HTMLContainer style={{ width: shape.props.w, height: shape.props.h }}>
        <StandardizedToolWrapper
          title="Embed"
          headerContent={headerContent}
          primaryColor={EmbedShape.PRIMARY_COLOR}
          isSelected={isSelected}
          width={shape.props.w}
          height={shape.props.h}
          onClose={handleClose}
          onMinimize={handleMinimize}
          isMinimized={isMinimized}
          onMaximize={toggleMaximize}
          isMaximized={isMaximized}
          editor={this.editor}
          shapeId={shape.id}
          isPinnedToView={shape.props.pinnedToView}
          onPinToggle={handlePinToggle}
          tags={shape.props.tags}
          onTagsChange={(newTags) => {
            this.editor.updateShape<IEmbedShape>({
              id: shape.id,
              type: 'Embed',
              props: {
                ...shape.props,
                tags: newTags,
              }
            })
          }}
          tagsEditable={true}
        >
          <div style={{
            height: '100%',
            overflow: 'hidden',
            backgroundColor: '#fff',
          }}>
            <iframe
              src={transformUrl(url)}
              width="100%"
              height="100%"
              style={{
                border: "none",
                display: 'block',
              }}
              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
              allowFullScreen
              loading="lazy"
              referrerPolicy="no-referrer"
              onLoad={(e) => {
                const iframe = e.currentTarget as HTMLIFrameElement
                if (!iframe) return;

                const messageHandler = (event: MessageEvent) => {
                  if (event.source === iframe.contentWindow) {
                    handleIframeInteraction(event.data)
                  }
                }

                window.addEventListener("message", messageHandler)

                return () => window.removeEventListener("message", messageHandler)
              }}
            />
          </div>
        </StandardizedToolWrapper>
      </HTMLContainer>
    )
  }

  override onDoubleClick = (shape: IEmbedShape) => {
    if (!shape.props.url) {
      const input = document.querySelector('input')
      input?.focus()
      return
    }

    if (
      shape.props.url.includes('medium.com') ||
      (shape.props.url && shape.props.url.match(/(?:twitter\.com|x\.com)\/[^\/]+$/))
    ) {
      window.top?.open(shape.props.url, '_blank', 'noopener,noreferrer')
      return
    }

    const iframe = document.querySelector(`[data-shape-id="${shape.id}"] iframe`) as HTMLIFrameElement
    if (iframe) {
      iframe.style.pointerEvents = 'all'
      const cleanup = () => {
        iframe.style.pointerEvents = 'none'
        window.removeEventListener('pointerdown', cleanup)
      }
      window.addEventListener('pointerdown', cleanup)
    }
  }

  onPointerDown = (shape: IEmbedShape) => {
    if (!shape.props.url) {
      const input = document.querySelector('input')
      input?.focus()
    }
  }

  override onBeforeCreate = (shape: IEmbedShape) => {
    if (shape.props.url) {
      const dimensions = getDefaultDimensions(shape.props.url)
      return {
        ...shape,
        props: {
          ...shape.props,
          w: dimensions.w,
          h: dimensions.h,
        },
      }
    }
    return shape
  }

  override onBeforeUpdate = (prev: IEmbedShape, next: IEmbedShape) => {
    if (next.props.url && prev.props.url !== next.props.url) {
      const dimensions = getDefaultDimensions(next.props.url)
      return {
        ...next,
        props: {
          ...next.props,
          w: dimensions.w,
          h: dimensions.h,
        },
      }
    }
    return next
  }
}
