/* global ResizeObserver */

import { trackEvent } from 'helpers/plausible'
import {
  borders,
  colors,
  layout,
  theme,
  space,
  SECTION_VERTICAL_SPACING
} from 'theme'
import React, { useState, useCallback, useEffect, useRef } from 'react'
import {
  Clipboard,
  Download,
  ExternalLink,
  Globe,
  ArrowRight,
  HelpCircle,
  Settings,
  Film
} from 'react-feather'
import isUrl from 'is-url-http/lightweight'
import prependHttp from 'prepend-http'
import styled from 'styled-components'
import get from 'dlv'
import mql from '@microlink/mql'

import Box from 'components/elements/Box'
import { Button } from 'components/elements/Button/Button'
import Caps from 'components/elements/Caps'
import Choose from 'components/elements/Choose'
import Container from 'components/elements/Container'
import DotSpinner from 'components/elements/DotSpinner'
import Flex from 'components/elements/Flex'
import HeadingBase from 'components/elements/Heading'
import Input from 'components/elements/Input/Input'
import { Link } from 'components/elements/Link'
import Meta from 'components/elements/Meta/Meta'
import Spinner from 'components/elements/Spinner'
import SubheadBase from 'components/elements/Subhead'
import Text from 'components/elements/Text'

import Block from 'components/patterns/Block/Block'
import CaptionBase from 'components/patterns/Caption/Caption'
import Faq from 'components/patterns/Faq/Faq'
import Features from 'components/patterns/Features/Features'
import Layout from 'components/patterns/Layout'
import Tooltip from 'components/patterns/Tooltip/Tooltip'

import { useClipboard } from 'components/hook/use-clipboard'
import { useLocalStorage } from 'components/hook/use-local-storage'
import {
  ApiErrorTitle,
  ApiErrorBody
} from 'components/patterns/ApiError/ApiError'
import { normalizeApiError, getErrorMeta } from 'helpers/api-error'
import { withTitle } from 'helpers/hoc/with-title'
import NerdStatsOverlay, {
  NerdStatsToggle,
  extractNerdStats,
  buildMqlQuery
} from 'components/patterns/NerdStats/NerdStats'

import {
  PanelSection,
  SectionLabel,
  OptionLabel,
  GenerateButton,
  CheckboxLabel,
  PreviewCanvas,
  ViewportCard,
  SkeletonPulse,
  FadeIn,
  StepCard,
  SectionIcon,
  UseCaseCard,
  ActionButton,
  ToolLayout,
  OptionsPanelOuter,
  PreviewOuter,
  PanelRibbonLayout,
  StickyGenerateWrapper,
  SegmentedControl,
  ApiDocsCard,
  PreviewEmptyState,
  ScreenshotHistory,
  downloadFile,
  createThumbnail,
  LAYOUT_PIVOT,
  MAX_HISTORY_ITEMS,
  HISTORY_MAX_AGE_MS
} from 'components/pages/screenshot'

const Heading = withTitle(HeadingBase)
const Subhead = withTitle(SubheadBase)
const Caption = withTitle(CaptionBase)

/* ─── Animated-specific layout extensions (min-height 550px) ─── */

const AnimatedOptionsPanelOuter = styled(OptionsPanelOuter)`
  @media (min-width: ${LAYOUT_PIVOT}px) {
    min-height: 550px;
  }
`

const AnimatedPreviewOuter = styled(PreviewOuter)`
  @media (min-width: ${LAYOUT_PIVOT}px) {
    min-height: 550px;
  }
`

/* ─── Constants ────────────────────────────────────────── */

const DEVICES = {
  desktop: { apiName: 'Macbook Pro 13', width: 1440, height: 900 },
  tablet: { apiName: 'iPad', width: 768, height: 1024 },
  mobile: { apiName: 'iPhone 13 Pro Max', width: 428, height: 926 }
}

const SCREENSHOT_HISTORY_KEY = 'screenshot-history/animated'
const MAX_SCREENSHOT_PREVIEW_HEIGHT = 750

const MIN_DURATION_S = 2
const MAX_DURATION_S = 10
const DEFAULT_DURATION_S = 5

const DEVICE_OPTIONS = [
  { value: 'desktop', label: 'Desktop' },
  { value: 'tablet', label: 'Tablet' },
  { value: 'mobile', label: 'Mobile' }
]

const FEATURES_LIST = [
  {
    title: 'Real Browser Recording',
    description:
      'Every animated screenshot is captured in a full Chromium instance — CSS animations, scroll effects, and JavaScript transitions are recorded exactly as a visitor would see them.'
  },
  {
    title: 'Configurable Duration',
    description:
      'Set the recording length from 2 to 10 seconds. Capture quick hero animations or longer scroll sequences — the slider lets you dial in exactly what you need.'
  },
  {
    title: 'Lightweight MP4 Output',
    description:
      'Animated screenshots are delivered as H.264 MP4 files that play natively in every browser. Small file sizes, no codec issues, no GIF bloat.'
  }
]

const HOW_IT_WORKS = [
  {
    icon: Globe,
    title: 'Paste a URL',
    description:
      'Enter any website address into the animated screenshot tool above.'
  },
  {
    icon: Settings,
    title: 'Pick Device & Duration',
    description: 'Choose desktop, tablet, or mobile and set how long to record.'
  },
  {
    icon: Film,
    title: 'Capture the Animation',
    description:
      'We open the page and record every animation, transition, and scroll effect as an MP4.'
  },
  {
    icon: Download,
    title: 'Download or Share',
    description:
      'Save the animated screenshot to your device or copy the CDN URL to use anywhere.'
  }
]

const REASON_TO_USE = [
  {
    title: 'True Animated Screen Capture',
    description: (
      <>
        Static screenshots miss CSS transitions, scroll effects, and{' '}
        <Link href='/docs/guides/screenshot/page-interaction'>
          interactive states
        </Link>
        . Our animated screenshot tool records the full live experience as an
        MP4 video. Need a static image instead? Use the{' '}
        <Link href='/tools/website-screenshot'>website screenshot tool</Link>.
      </>
    )
  },
  {
    title: 'Screenshot Any Animated Page',
    description: (
      <>
        Works on any public webpage — marketing sites, dashboards, product
        demos, portfolios. For{' '}
        <Link href='/docs/guides/screenshot/private-pages'>
          pages behind a login
        </Link>
        , use the API with custom headers or cookies.
      </>
    )
  },
  {
    title: 'Free Online Animated Screenshot',
    description:
      'Take animated screenshots online with no software to install and no account required. Just paste the URL and capture.'
  },
  {
    title: '25 Free Captures per Day',
    description:
      'Every animated screenshot is clean and professional — no watermarks, no overlays, no branding. Free for up to 25 captures daily.'
  },
  {
    title: 'Desktop, Tablet & Mobile',
    description: (
      <>
        Record animated screenshots on any viewport. For pixel-perfect phone
        emulation with real device frames, try the{' '}
        <Link href='/tools/website-screenshot/mobile'>
          mobile screenshot tool
        </Link>
        .
      </>
    )
  },
  {
    title: 'Clean Captures, No Ads',
    description: (
      <>
        Cookie banners and ad overlays are automatically removed before the
        animated web page is captured. Learn more in the{' '}
        <Link href='/docs/guides/screenshot/page-interaction#start-with-the-cleanest-page'>
          browser settings guide
        </Link>
        .
      </>
    )
  }
]

/* ─── Options Panel ────────────────────────────────────── */

const OptionsPanel = ({ options, setOptions, onSubmit, isLoading }) => {
  const [urlError, setUrlError] = useState('')

  const handleUrlChange = useCallback(
    e => {
      const val = e.target.value
      setOptions(prev => ({ ...prev, url: val }))
      if (urlError) setUrlError('')
    },
    [setOptions, urlError]
  )

  const normalizeUrl = useCallback(rawUrl => {
    const trimmed = rawUrl.trim()
    if (!trimmed) return ''
    return prependHttp(trimmed)
  }, [])

  const handleSubmit = useCallback(() => {
    const url = normalizeUrl(options.url)
    if (!url || !isUrl(url)) {
      setUrlError('Please enter a valid URL (e.g., example.com)')
      return
    }

    setOptions(prev => ({ ...prev, url }))
    setUrlError('')
    onSubmit(url)
  }, [options.url, onSubmit, normalizeUrl, setOptions])

  const handleDeviceChange = useCallback(
    val => {
      if (DEVICES[val]) {
        setOptions(prev => ({ ...prev, device: val }))
      }
    },
    [setOptions]
  )

  return (
    <Box
      css={theme({
        p: [3, 4],
        border: 1,
        borderColor: 'black10',
        borderRadius: 3
      })}
    >
      {/* ── Primary Input ───────────────────── */}
      <PanelSection>
        <OptionLabel as='span'>Website URL</OptionLabel>
        <Input
          id='ws-url'
          type='url'
          inputMode='url'
          autoComplete='url'
          placeholder='example.com'
          value={options.url}
          onChange={handleUrlChange}
          onKeyDown={e => {
            if (e.key === 'Enter') {
              if (isLoading) {
                e.preventDefault()
                return
              }
              e.preventDefault()
              handleSubmit()
            }
          }}
          css={theme({ width: '100%', fontSize: '18px' })}
          aria-describedby={urlError ? 'ws-url-error' : undefined}
          aria-invalid={!!urlError}
        />
        {urlError && (
          <Text
            id='ws-url-error'
            role='alert'
            css={theme({ color: 'fullscreen', fontSize: 0, pt: 1 })}
          >
            {urlError}
          </Text>
        )}
      </PanelSection>

      {/* ── Settings Groups (Ribbon on tablet) ── */}
      <PanelRibbonLayout>
        {/* ── Display Settings ────────────────── */}
        <PanelSection>
          <OptionLabel as='span'>Device</OptionLabel>
          <SegmentedControl
            name='Device'
            options={DEVICE_OPTIONS}
            value={options.device}
            onChange={handleDeviceChange}
          />
        </PanelSection>

        {/* ── Duration ────────────────────────── */}
        <PanelSection>
          <OptionLabel as='span'>Animation duration</OptionLabel>
          <Flex
            css={{ alignItems: 'center', gap: space[2], marginTop: space[2] }}
          >
            <Box css={{ flex: 1 }}>
              <input
                id='ws-duration'
                type='range'
                min={MIN_DURATION_S}
                max={MAX_DURATION_S}
                step={1}
                value={Number(options.duration) || DEFAULT_DURATION_S}
                onChange={e =>
                  setOptions(prev => ({ ...prev, duration: e.target.value }))}
                aria-label='Animation duration in seconds'
                style={{ width: '100%', accentColor: colors.link }}
              />
            </Box>
            <Input
              id='ws-duration-input'
              type='number'
              inputMode='numeric'
              min={MIN_DURATION_S}
              max={MAX_DURATION_S}
              step={1}
              placeholder={String(DEFAULT_DURATION_S)}
              aria-label='Animation duration in seconds'
              value={options.duration}
              onChange={e => {
                const raw = e.target.value
                setOptions(prev => ({ ...prev, duration: raw }))
              }}
              onBlur={() => {
                const n = Number(options.duration)
                if (!n || n < MIN_DURATION_S) {
                  setOptions(prev => ({
                    ...prev,
                    duration: String(MIN_DURATION_S)
                  }))
                } else if (n > MAX_DURATION_S) {
                  setOptions(prev => ({
                    ...prev,
                    duration: String(MAX_DURATION_S)
                  }))
                }
              }}
              css={theme({
                width: '70px',
                fontSize: 1,
                height: '18px',
                textAlign: 'center'
              })}
            />
            <Text
              css={theme({
                fontFamily: 'sans',
                fontSize: 0,
                color: 'black50',
                flexShrink: 0
              })}
            >
              s
            </Text>
          </Flex>
        </PanelSection>

        {/* ── Advanced ────────────────────────── */}
        <Box css={theme({ pb: 3 })}>
          <SectionLabel>Advanced</SectionLabel>

          <CheckboxLabel>
            <input
              type='checkbox'
              checked={options.adblock}
              onChange={e =>
                setOptions(prev => ({ ...prev, adblock: e.target.checked }))}
            />
            <Text css={theme({ pl: 2, fontSize: 1, color: 'black80' })}>
              Block ads and banners
            </Text>
            <Tooltip
              content={
                <Tooltip.Content>
                  Removes all the ads and cookie banners before the recording
                </Tooltip.Content>
              }
            >
              <HelpCircle
                size={16}
                color={colors.black60}
                style={{ marginLeft: '6px', marginTop: '5px' }}
              />
            </Tooltip>
          </CheckboxLabel>

          <CheckboxLabel>
            <input
              type='checkbox'
              checked={options.cache}
              onChange={e =>
                setOptions(prev => ({ ...prev, cache: e.target.checked }))}
            />
            <Text css={theme({ pl: 2, fontSize: 1, color: 'black80' })}>
              Use cache
            </Text>
            <Tooltip
              content={
                <Tooltip.Content>
                  Uses the cached recording if available, otherwise generates a
                  new one
                </Tooltip.Content>
              }
            >
              <HelpCircle
                size={16}
                color={colors.black60}
                style={{ marginLeft: '6px', marginTop: '5px' }}
              />
            </Tooltip>
          </CheckboxLabel>
        </Box>
      </PanelRibbonLayout>

      {/* ── Generate ────────────────────────── */}
      <StickyGenerateWrapper css={{ textAlign: 'center', marginTop: '10px' }}>
        <GenerateButton
          type='button'
          onClick={handleSubmit}
          loading={isLoading}
        >
          <Flex
            css={{
              alignItems: 'center',
              justifyContent: 'center',
              gap: space[2]
            }}
          >
            <Film size={16} />
            Generate animated screenshot
          </Flex>
        </GenerateButton>
      </StickyGenerateWrapper>
    </Box>
  )
}

/* ─── Preview Panel (video-based, differs from shared image PreviewDisplay) ─ */

const PreviewDisplay = ({
  data,
  isLoading,
  error,
  onRetry,
  url,
  viewportWidth,
  viewportHeight,
  nerdStats,
  mqlQuery,
  responseData,
  showNerdStats,
  onToggleNerdStats
}) => {
  const [ClipboardComponent, toClipboard] = useClipboard()
  const [videoPainted, setVideoPainted] = useState(false)
  const prevVideoUrlRef = useRef(null)
  const scrollAreaRef = useRef(null)

  const videoUrl = get(data, 'screenshot.animated.url')
  const previewImageUrl = get(data, 'screenshot.url')

  useEffect(() => {
    if (showNerdStats && scrollAreaRef.current) {
      scrollAreaRef.current.scrollTop = 0
    }
  }, [showNerdStats])

  const viewportCardRef = useRef(null)
  const [actualWidth, setActualWidth] = useState(0)

  useEffect(() => {
    if (!viewportCardRef.current) return

    const updateWidth = () => {
      if (viewportCardRef.current) {
        setActualWidth(viewportCardRef.current.offsetWidth - 32)
      }
    }

    updateWidth()

    const resizeObserver = new ResizeObserver(updateWidth)
    resizeObserver.observe(viewportCardRef.current)

    return () => resizeObserver.disconnect()
  }, [])

  const maxWidthScaled = (viewportWidth * 2) / 3
  const maxWidth = maxWidthScaled > actualWidth ? actualWidth : maxWidthScaled

  const fractionLost = (viewportWidth - maxWidth) / viewportWidth
  const maxHeightScaled = viewportHeight - viewportHeight * fractionLost
  const maxHeight =
    maxHeightScaled > MAX_SCREENSHOT_PREVIEW_HEIGHT
      ? MAX_SCREENSHOT_PREVIEW_HEIGHT
      : maxHeightScaled

  const containerHeight = maxHeight + 32

  const showSkeleton = isLoading || (!!videoUrl && !videoPainted)

  useEffect(() => {
    if (isLoading) {
      setVideoPainted(false)
    }
  }, [isLoading])

  useEffect(() => {
    if (videoUrl && videoUrl !== prevVideoUrlRef.current) {
      prevVideoUrlRef.current = videoUrl
      setVideoPainted(false)
    }
  }, [videoUrl])

  return (
    <PreviewCanvas ref={viewportCardRef}>
      <Choose>
        {/* ── Loading / waiting for video ─── */}
        <Choose.When condition={showSkeleton}>
          <FadeIn
            key='loading'
            css={theme({
              display: 'flex',
              flexDirection: 'column',
              height: '100%'
            })}
          >
            <Box
              css={theme({
                p: [3],
                flex: 1,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                position: 'relative'
              })}
            >
              {videoUrl && !isLoading && (
                <video
                  src={videoUrl}
                  poster={previewImageUrl}
                  onLoadedData={() => setVideoPainted(true)}
                  style={{
                    position: 'absolute',
                    opacity: 0,
                    pointerEvents: 'none',
                    width: 0,
                    height: 0
                  }}
                />
              )}
              <ViewportCard
                style={{
                  maxWidth: `${maxWidth}px`,
                  maxHeight: `${maxHeight}px`
                }}
              >
                <SkeletonPulse
                  role='progressbar'
                  aria-label={
                    isLoading
                      ? 'Recording animated screenshot'
                      : 'Loading video'
                  }
                  style={{
                    width: '100%',
                    height: `${maxHeight}px`
                  }}
                />
              </ViewportCard>
            </Box>
            <Flex
              css={theme({
                p: 3,
                gap: 2,
                borderTop: 1,
                borderColor: 'black05',
                bg: 'white',
                justifyContent: 'center',
                alignItems: 'center'
              })}
              aria-live='polite'
              aria-label={
                isLoading ? 'Recording animated screenshot' : 'Loading video'
              }
            >
              <Spinner width='20px' height='14px' />
              <Text
                css={theme({
                  color: 'black50',
                  fontSize: 1,
                  fontFamily: 'sans'
                })}
              >
                {isLoading
                  ? (
                    <>
                      Recording animated screenshot
                      <DotSpinner />
                    </>
                    )
                  : (
                    <>
                      Loading video
                      <DotSpinner />
                    </>
                    )}
              </Text>
            </Flex>
          </FadeIn>
        </Choose.When>

        {/* ── Error state ────────────────────── */}
        <Choose.When condition={!!error}>
          <FadeIn
            key='error'
            css={theme({
              display: 'flex',
              flexDirection: 'column',
              alignItems: 'center',
              justifyContent: 'center',
              minHeight: ['380px', '380px', '520px'],
              px: 4,
              textAlign: 'center'
            })}
          >
            <Text css={theme({ color: 'fullscreen', fontSize: 3, pb: 3 })}>
              <ApiErrorTitle code={error?.code} />
              <Text css={theme({ fontSize: 2, color: 'black60', pt: 2 })}>
                <ApiErrorBody
                  code={error?.code}
                  fallback={
                    error?.message || 'Something went wrong. Please try again.'
                  }
                />
              </Text>
            </Text>
            {getErrorMeta(error?.code).showRetry && (
              <Button onClick={onRetry}>
                <Caps css={theme({ fontSize: 0 })}>Try again</Caps>
              </Button>
            )}
          </FadeIn>
        </Choose.When>

        {/* ── Video result ──────────────── */}
        <Choose.When condition={!!videoUrl && videoPainted}>
          <FadeIn
            key='result'
            css={theme({
              display: 'flex',
              flexDirection: 'column',
              height: '100%'
            })}
          >
            <Box
              ref={scrollAreaRef}
              css={theme({
                p: [3],
                flex: 1,
                overflowY: 'auto',
                overflowX: 'hidden',
                minHeight: `${containerHeight}px`,
                maxHeight: ['60vh', '750px', '750px'],
                WebkitOverflowScrolling: 'touch',
                position: 'relative'
              })}
            >
              {showNerdStats && nerdStats && (
                <NerdStatsOverlay
                  stats={nerdStats}
                  mqlQuery={mqlQuery}
                  responseData={responseData}
                />
              )}
              <ViewportCard style={{ maxWidth: `${maxWidth}px` }}>
                <video
                  src={videoUrl}
                  poster={previewImageUrl}
                  autoPlay
                  loop
                  muted
                  playsInline
                  style={{
                    width: '100%',
                    maxWidth: '100%',
                    display: 'block',
                    objectFit: 'contain'
                  }}
                >
                  <track kind='descriptions' label='Animated screenshot' />
                </video>
              </ViewportCard>
            </Box>

            {/* Action buttons bar */}
            <Flex
              css={theme({
                p: 3,
                gap: 2,
                borderTop: 1,
                borderColor: 'black05',
                bg: 'white',
                flexWrap: 'wrap'
              })}
            >
              <ActionButton
                role='button'
                tabIndex={0}
                onClick={e => {
                  e.preventDefault()
                  trackEvent('screenshot download', { variant: 'animated' })
                  downloadFile(
                    videoUrl,
                    `animated-screenshot-${Date.now()}.mp4`
                  )
                }}
                css={theme({
                  bg: 'black',
                  color: 'white',
                  _hover: { bg: 'black80' }
                })}
              >
                <Download size={15} />
                <Caps css={theme({ fontSize: 0 })}>Download</Caps>
              </ActionButton>

              <ActionButton
                as='button'
                type='button'
                onClick={() => {
                  trackEvent('screenshot copy url', { variant: 'animated' })
                  toClipboard({
                    copy: videoUrl,
                    text: Tooltip.TEXT.COPIED('URL')
                  })
                }}
                css={theme({
                  bg: 'white',
                  color: 'black80',
                  border: 1,
                  borderColor: 'black10',
                  width: '100%',
                  _hover: { bg: 'gray1', borderColor: 'black20' }
                })}
              >
                <Clipboard size={15} />
                <Caps css={theme({ fontSize: 0 })}>Copy URL</Caps>
              </ActionButton>

              <ActionButton
                href={videoUrl}
                target='_blank'
                rel='noopener noreferrer'
                aria-label='Open animated screenshot in new tab'
                css={theme({
                  bg: 'white',
                  color: 'black80',
                  border: 1,
                  borderColor: 'black10',
                  _hover: { bg: 'gray1', borderColor: 'black20' }
                })}
              >
                <ExternalLink size={15} />
                <Caps css={theme({ fontSize: 0 })}>Open</Caps>
              </ActionButton>
              {nerdStats && (
                <NerdStatsToggle
                  active={showNerdStats}
                  onClick={onToggleNerdStats}
                />
              )}
            </Flex>
          </FadeIn>
          <ClipboardComponent />
        </Choose.When>

        {/* ── Empty state ────────────────────── */}
        <Choose.Otherwise>
          <FadeIn
            key='empty'
            css={theme({
              display: 'flex',
              flexDirection: 'column',
              alignItems: 'center',
              justifyContent: 'center',
              minHeight: ['380px', '380px', '520px'],
              px: 4,
              textAlign: 'center'
            })}
          >
            <PreviewEmptyState
              icon={Film}
              text='Enter a URL and click Generate'
              subtext='Your animated screenshot will appear here'
            />
          </FadeIn>
        </Choose.Otherwise>
      </Choose>
    </PreviewCanvas>
  )
}

/* ─── Main Tool Section ────────────────────────────────── */

const AnimatedScreenshotTool = () => {
  const [options, setOptions] = useState({
    url: '',
    adblock: true,
    cache: true,
    device: 'desktop',
    duration: String(DEFAULT_DURATION_S)
  })

  const [isLoading, setIsLoading] = useState(false)
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)
  const [lastUrl, setLastUrl] = useState('')
  const [nerdStats, setNerdStats] = useState(null)
  const [mqlQuery, setMqlQuery] = useState(null)
  const [responseData, setResponseData] = useState(null)
  const [showNerdStats, setShowNerdStats] = useState(false)
  const [requestedViewport, setRequestedViewport] = useState({
    width: DEVICES.desktop.width,
    height: DEVICES.desktop.height
  })

  const [localStorageData] = useLocalStorage('mql-api-key')
  const [history, setHistory] = useLocalStorage(SCREENSHOT_HISTORY_KEY, [])
  const [activeHistoryId, setActiveHistoryId] = useState(null)
  const [historyReady, setHistoryReady] = useState(false)

  useEffect(() => {
    setHistory(prev => {
      if (!Array.isArray(prev)) return []
      const now = Date.now()
      const cleaned = prev.filter(
        entry => now - entry.createdAt < HISTORY_MAX_AGE_MS
      )
      return cleaned.length !== prev.length ? cleaned : prev
    })
    setHistoryReady(true)
  }, [setHistory])

  const handleSubmit = useCallback(
    async url => {
      trackEvent('screenshot generate', { variant: 'animated' })
      const deviceDef = DEVICES[options.device] || DEVICES.desktop
      setRequestedViewport({ width: deviceDef.width, height: deviceDef.height })
      setIsLoading(true)
      setError(null)
      setData(null)
      setShowNerdStats(false)
      setLastUrl(url)

      try {
        const durationS = Math.min(
          Math.max(
            Number(options.duration) || DEFAULT_DURATION_S,
            MIN_DURATION_S
          ),
          MAX_DURATION_S
        )

        const mqlOpts = {
          apiKey: localStorageData?.apiKey,
          meta: false,
          screenshot: {
            animated: {
              duration: durationS * 1000
            }
          },
          device: deviceDef.apiName,
          adblock: options.adblock,
          force: !options.cache
        }

        const queryStr = buildMqlQuery(url, mqlOpts)
        setMqlQuery(queryStr)

        let response = null
        let headerStats = null
        try {
          response = await mql(url, mqlOpts)
          setData(response.data)
          headerStats = extractNerdStats(response.response?.headers)
          setNerdStats(headerStats)
          setResponseData(
            JSON.stringify(
              { status: response.status, data: response.data },
              null,
              2
            )
          )
        } catch (err) {
          setError(
            normalizeApiError.fromMql(
              err,
              'Failed to capture animated screenshot.'
            )
          )
        }

        if (response?.data?.screenshot) {
          const entryId = String(Date.now())
          const previewImage = response.data.screenshot.url
          const thumbnail = await createThumbnail(previewImage)
          setHistory(prev => {
            const items = Array.isArray(prev) ? prev : []
            return [
              {
                id: entryId,
                createdAt: Date.now(),
                screenshot: response.data.screenshot,
                previewImage,
                thumbnail,
                nerdStats: headerStats,
                mqlQuery: queryStr,
                responseData: JSON.stringify(
                  { status: response.status, data: response.data },
                  null,
                  2
                ),
                settings: {
                  url,
                  device: options.device,
                  duration: options.duration,
                  adblock: options.adblock,
                  cache: options.cache
                }
              },
              ...items
            ].slice(0, MAX_HISTORY_ITEMS)
          })
          setActiveHistoryId(entryId)
        }
      } catch (err) {
        setError(
          normalizeApiError.fromMql(
            err,
            'Failed to capture animated screenshot.'
          )
        )
      } finally {
        setIsLoading(false)
      }
    },
    [options, setHistory, localStorageData]
  )

  const handleHistorySelect = useCallback(entry => {
    const { settings, screenshot } = entry
    const deviceKey = settings.device || 'desktop'
    const deviceDef = DEVICES[deviceKey] || DEVICES.desktop
    setOptions({
      url: settings.url,
      adblock: settings.adblock !== undefined ? settings.adblock : true,
      cache: settings.cache !== undefined ? settings.cache : true,
      device: deviceKey,
      duration: settings.duration || String(DEFAULT_DURATION_S)
    })
    setData({ screenshot })
    setLastUrl(settings.url)
    setNerdStats(entry.nerdStats || null)
    setMqlQuery(entry.mqlQuery || null)
    setResponseData(entry.responseData || null)
    setRequestedViewport({
      width: deviceDef.width,
      height: deviceDef.height
    })
    setError(null)
    setActiveHistoryId(entry.id)
  }, [])

  const handleHistoryDelete = useCallback(
    id => {
      setHistory(prev => {
        const items = Array.isArray(prev) ? prev : []
        return items.filter(entry => entry.id !== id)
      })
      setActiveHistoryId(prev => (prev === id ? null : prev))
    },
    [setHistory]
  )

  const handleRetry = useCallback(() => {
    if (lastUrl) handleSubmit(lastUrl)
  }, [lastUrl, handleSubmit])

  return (
    <Container
      as='section'
      id='tool'
      css={theme({
        px: ['16px', '25px'],
        maxWidth: ['100%', layout.normal, '1460px', '1460px'],
        py: SECTION_VERTICAL_SPACING
      })}
    >
      <ToolLayout>
        <AnimatedOptionsPanelOuter>
          <OptionsPanel
            options={options}
            setOptions={setOptions}
            onSubmit={handleSubmit}
            isLoading={isLoading}
          />
        </AnimatedOptionsPanelOuter>

        <AnimatedPreviewOuter>
          <PreviewDisplay
            data={data}
            isLoading={isLoading}
            error={error}
            onRetry={handleRetry}
            url={lastUrl}
            viewportWidth={requestedViewport.width}
            viewportHeight={requestedViewport.height}
            nerdStats={nerdStats}
            mqlQuery={mqlQuery}
            responseData={responseData}
            showNerdStats={showNerdStats}
            onToggleNerdStats={() => setShowNerdStats(prev => !prev)}
          />
        </AnimatedPreviewOuter>
      </ToolLayout>

      {historyReady && (
        <ScreenshotHistory
          entries={Array.isArray(history) ? history : []}
          activeId={activeHistoryId}
          onSelect={handleHistorySelect}
          onDelete={handleHistoryDelete}
          disabled={isLoading}
          entityLabel='recording'
          getThumbnailSrc={entry => entry.thumbnail || entry.previewImage}
        />
      )}
    </Container>
  )
}

/* ─── Hero Section ─────────────────────────────────────── */

const Hero = () => (
  <Flex
    as='section'
    id='hero'
    css={theme({
      flexDirection: 'column',
      alignItems: 'center',
      pt: [1],
      pb: SECTION_VERTICAL_SPACING
    })}
  >
    <Heading
      css={theme({
        px: [3, 3],
        maxWidth: layout.large
      })}
    >
      Animated Screenshot Tool
    </Heading>
    <Caption
      forwardedAs='h2'
      css={theme({
        pt: [2, 2, 3, 3],
        px: 3,
        maxWidth: layout.large
      })}
    >
      Capture any animated web page as a video. Free, online, no sign-up.
    </Caption>
  </Flex>
)

/* ─── How It Works ─────────────────────────────────────── */

const HowItWorks = () => (
  <Container
    as='section'
    id='how-it-works'
    css={theme({
      alignItems: 'center',
      width: '100%',
      py: SECTION_VERTICAL_SPACING
    })}
  >
    <Subhead
      css={theme({
        pt: [3, 3, 4, 4],
        px: 3,
        maxWidth: layout.large
      })}
    >
      How to take an animated screenshot of a webpage
    </Subhead>
    <Flex
      css={theme({
        flexDirection: 'row',
        flexWrap: 'wrap',
        justifyContent: 'center',
        gap: [3, 3, 4, 4],
        pt: [2, 2, 3, 3]
      })}
    >
      {HOW_IT_WORKS.map(({ icon: Icon, title, description }) => (
        <StepCard key={title}>
          <SectionIcon icon={Icon} />
          <Caps as='h3' css={theme({ fontWeight: 'bold', pb: 2, fontSize: 0 })}>
            {title}
          </Caps>
          <Text css={theme({ fontSize: 1, color: 'black60', lineHeight: 2 })}>
            {description}
          </Text>
        </StepCard>
      ))}
    </Flex>
  </Container>
)

/* ─── Explanation ────────────────────────────────────── */

const Explanation = () => (
  <Container
    as='section'
    id='why-choose'
    css={theme({
      alignItems: 'center',
      py: SECTION_VERTICAL_SPACING,
      bg: 'pinky'
    })}
  >
    <Subhead variant='gradient'>
      Why use our online animated screenshot tool?
    </Subhead>
    <Box
      css={theme({
        display: 'grid',
        gridTemplateColumns: ['1fr', '1fr', '1fr 1fr', '1fr 1fr 1fr'],
        gap: 3,
        pt: [4, 4, 5, 5],
        maxWidth: [layout.normal, layout.normal, layout.large, layout.large]
      })}
    >
      {REASON_TO_USE.map(({ title, description }) => (
        <UseCaseCard key={title}>
          <Caps as='h3' css={theme({ fontWeight: 'bold', pb: 2, fontSize: 1 })}>
            {title}
          </Caps>
          <Text css={theme({ fontSize: 1, color: 'black60', lineHeight: 2 })}>
            {description}
          </Text>
        </UseCaseCard>
      ))}
    </Box>
    <Caption
      css={theme({
        pt: [4, 4, 5, 5],
        px: [1, 1, 3, 3],
        maxWidth: layout.large
      })}
    >
      <Text css={theme({ fontSize: 3, color: 'black' })}>
        How do we offer this level of quality for free?
      </Text>
      <Text
        css={theme({ fontSize: 2, color: 'black80', lineHeight: 2, mt: 2 })}
      >
        This animated screen capture tool runs on <b>Microlink's</b>{' '}
        <Link href='/screenshot'>screenshot API</Link> — the same infrastructure
        used by thousands of paying customers to capture millions of animated
        screenshots every week. You get enterprise performance at zero cost.
      </Text>
    </Caption>
  </Container>
)

/* ─── Banner ─────────────────────────────────────────── */

const Banner = () => (
  <Block
    forwardedAs='section'
    id='pricing'
    flexDirection='column'
    css={theme({
      px: 4,
      maxHeight: '800px',
      py: SECTION_VERTICAL_SPACING,
      width: '100%',
      overflow: 'hidden',
      backgroundImage: `radial-gradient(
        circle at center right,
        #850ba7 0%,
        #850ba7 48%,
        #a31b91 48%,
        #a31b91 52%,
        #c12a78 52%,
        #c12a78 65%,
        #df3a61 65%,
        #df3a61 79%,
        #fd494a 79%,
        #fd494a 100%
      )`,
      borderTop: `${borders[1]} ${colors.white20}`,
      borderBottom: `${borders[1]} ${colors.white20}`
    })}
    blockOne={
      <Flex
        css={{
          flexDirection: 'column',
          justifyContent: 'center',
          alignItems: 'center'
        }}
      >
        <Subhead css={theme({ color: 'white' })}>
          Animated Screenshot API{' '}
          <span css={theme({ display: 'block', color: 'white60' })}>
            for developers
          </span>
        </Subhead>
      </Flex>
    }
    blockTwo={
      <Flex
        css={theme({
          pt: [4, 4, 5, 5],
          justifyContent: 'center',
          alignItems: 'flex-start',
          maxHeight: ['200px', '300px', '400px', '650px'],
          overflow: 'hidden'
        })}
        style={{ fontVariantNumeric: 'tabular-nums' }}
      >
        <Link href='/screenshot' aria-label='Microlink Screenshot API'>
          <img
            css={theme({
              width: ['300px', '500px', '700px', '900px']
            })}
            src='/images/screenshot-tool-landing.png'
            alt='Microlink animated screenshot API'
          />
        </Link>
      </Flex>
    }
  />
)

/* ─── Use Cases ───────────────────────────────────────── */

const USE_CASES = [
  {
    title: 'For Product Teams',
    items: [
      'Take animated screenshots of feature flows and onboarding',
      'Capture interactive UI states that static images miss',
      'Record animated walkthroughs for stakeholder presentations',
      'Screenshot animated pages to document product behavior'
    ],
    link: {
      href: '/docs/guides/screenshot/page-interaction',
      alt: 'Page interaction guide',
      text: 'Page interaction guide'
    }
  },
  {
    title: 'For Digital Marketers',
    items: [
      'Print screen animated web pages for competitor research',
      'Capture animated landing pages for ad creative analysis',
      'Generate animated webpage screenshots for social content',
      'Screenshot website animations for campaign inspiration'
    ],
    link: {
      href: '/docs/guides/screenshot/embedding',
      alt: 'Embedding screenshots',
      text: 'Embedding guide'
    }
  },
  {
    title: 'For Developers',
    items: [
      'Automate animated screen capture via a simple REST API',
      'Record animated page screenshots for visual regression tests',
      'Capture CSS transitions and scroll effects on any webpage',
      'Screenshot animated pages for automated documentation'
    ],
    link: {
      href: '/docs/api/parameters/screenshot',
      alt: 'Screenshot API reference',
      text: 'API reference'
    }
  }
]

const UseCases = () => (
  <Container
    as='section'
    id='use-cases'
    css={theme({
      alignItems: 'center',
      maxWidth: [layout.normal, layout.normal, layout.large, layout.large],
      py: SECTION_VERTICAL_SPACING
    })}
  >
    <Subhead variant='gradient'>
      Who uses our animated screen capture tool?
    </Subhead>
    <Caption css={theme({ pt: [3, 3, 4, 4], maxWidth: layout.normal })}>
      From print-screen animated web pages to automated API capture, teams
      across every discipline rely on animated screenshots.
    </Caption>
    <Box
      css={theme({
        display: 'grid',
        gridTemplateColumns: ['1fr', '1fr', '1fr 1fr 1fr', '1fr 1fr 1fr'],
        gap: 3,
        pt: [4, 4, 5, 5],
        width: '100%'
      })}
    >
      {USE_CASES.map(({ title, items, link }) => (
        <Box
          key={title}
          css={theme({
            p: 4,
            border: 1,
            borderColor: 'black10',
            borderRadius: 3,
            bg: 'white'
          })}
        >
          <Caps
            as='h3'
            titleize={false}
            css={theme({ fontWeight: 'bold', pb: 3, fontSize: 1 })}
          >
            {title}
          </Caps>
          <Box
            as='ul'
            css={{
              padding: 0,
              margin: 0,
              listStyle: 'none',
              display: 'flex',
              flexDirection: 'column',
              gap: space[2]
            }}
          >
            {items.map(item => (
              <Flex
                key={item}
                as='li'
                css={{
                  alignItems: 'baseline',
                  gap: space[2]
                }}
              >
                <ArrowRight
                  size={12}
                  color={colors.link}
                  css={{ flexShrink: 0, position: 'relative', top: 1 }}
                />
                <Text
                  css={theme({
                    fontSize: 1,
                    color: 'black60',
                    lineHeight: 2
                  })}
                >
                  {item}
                </Text>
              </Flex>
            ))}
            <Flex css={theme({ pt: 3, justifyContent: 'flex-start' })}>
              <Link alt={link.alt} href={link.href}>
                {link.text}
              </Link>
            </Flex>
          </Box>
        </Box>
      ))}
    </Box>
  </Container>
)

/* ─── Product Information (FAQ) ────────────────────────── */

const ProductInformation = () => (
  <Faq
    css={theme({
      fontSize: [1, 1, 1, 1],
      py: SECTION_VERTICAL_SPACING,
      bg: 'pinky',
      borderTop: `${borders[1]} ${colors.pinkest}`,
      borderBottom: `${borders[1]} ${colors.pinkest}`
    })}
    questions={[
      {
        question: 'Is this animated screenshot tool really free?',
        answer: (
          <>
            <div>
              Yes. You can take up to{' '}
              <b>25&nbsp;animated screenshots per day</b> at no cost — no credit
              card, no account required. Every free capture includes all
              features: device emulation, ad blocking, and CDN caching. Need
              multiple.
            </div>
            <div>
              Need higher limits? See our{' '}
              <Link href='/pricing'>pricing plans</Link> for unlimited captures
              and priority processing.
            </div>
          </>
        )
      },
      {
        question: 'How do I take an animated screenshot of a webpage?',
        answer: (
          <>
            <div>
              Paste the URL into the tool above, choose a device (desktop,
              tablet, or mobile), set the recording duration, and click
              Generate. We open the page in a real browser, capture the animated
              web page — including CSS transitions, scroll effects, and
              interactive states — and return an MP4 video within seconds.
            </div>
          </>
        )
      },
      {
        question: 'How long can an animated screenshot be?',
        answer: (
          <>
            <div>
              You can record between <b>2&nbsp;and 10&nbsp;seconds</b>. The
              default is 5&nbsp;seconds.
            </div>
            <div>
              For pages with delayed animations or slow scroll interactions,
              increase the duration using the slider in the options panel.
            </div>
          </>
        )
      },
      {
        question:
          'Can I take a full page animated screenshot of a long web page?',
        answer: (
          <>
            <div>
              The tool captures the visible viewport for the selected duration.
              For long web pages, increase the recording time so more of the
              page content and animations are captured. On the{' '}
              <Link href='/docs/api/parameters/screenshot'>API</Link>, you have
              full control over duration and viewport to capture entire animated
              pages programmatically. For a static full-length capture, try the{' '}
              <Link href='/tools/website-screenshot/full-page'>
                full-page screenshot tool
              </Link>
              .
            </div>
          </>
        )
      },
      {
        question: 'What format are animated screenshots saved in?',
        answer: (
          <div>
            All animated screenshots are delivered as MP4 files using the H.264
            codec. MP4 plays natively in every major browser and device, keeping
            file sizes small without sacrificing quality. See the{' '}
            <Link href='/docs/guides/screenshot/customizing-output'>
              customizing output guide
            </Link>{' '}
            for more format options via the API.
          </div>
        )
      },
      {
        question: 'Can I automate animated screen capture via API?',
        answer: (
          <>
            <div>
              Yes. The tool is powered by the{' '}
              <Link href='/docs/api/parameters/screenshot'>
                Microlink Screenshot API
              </Link>
              , which accepts a single REST call. Add{' '}
              <code>screenshot: {'{ animated: true }'}</code> to capture any
              animated webpage programmatically from Node.js, Python, Ruby, or
              plain cURL. See the{' '}
              <Link href='/docs/guides/screenshot/embedding'>
                embedding guide
              </Link>{' '}
              for drop-in HTML snippets.
            </div>
            <div>
              Use the{' '}
              <Link href='https://www.npmjs.com/package/@microlink/mql'>
                @microlink/mql
              </Link>{' '}
              SDK for the easiest integration, or call the HTTP endpoint
              directly. If something goes wrong, check the{' '}
              <Link href='/docs/guides/screenshot/troubleshooting'>
                troubleshooting guide
              </Link>
              .
            </div>
          </>
        )
      },
      {
        question: 'How does caching work?',
        answer: (
          <>
            <div>
              Animated screenshots are cached on our global CDN for 24 hours by
              default. Cached responses are served instantly and{' '}
              <b>don't count against your daily limit</b>. Read the{' '}
              <Link href='/docs/guides/screenshot/caching-and-performance'>
                caching &amp; performance guide
              </Link>{' '}
              for fine-grained control.
            </div>
            <div>
              Turn off caching only if you need a fresh capture of a page that
              updates frequently.
            </div>
          </>
        )
      },
      {
        question: 'Any questions or issues?',
        answer: (
          <>
            <span>
              We are always available at:{' '}
              <Link href='mailto:hello@microlink.io'>hello@microlink.io</Link>
            </span>
          </>
        )
      }
    ]}
  />
)

/* ─── Page Head (SEO) ──────────────────────────────────── */

export const Head = () => (
  <Meta
    title='Animated Screenshot Tool — Free Online Animated Screen Capture'
    noSuffix
    description='Take a free animated screenshot of any website online. Capture animated web pages as MP4 video — no login, no install. Our online animated screen capture tool records any webpage in seconds.'
    schemaType='SoftwareApplication'
    structured={[
      {
        '@context': 'https://schema.org',
        '@type': 'SoftwareApplication',
        '@id': 'https://microlink.io/tools/website-screenshot/animated',
        name: 'Microlink Animated Screenshot Tool',
        description:
          'Free online animated screenshot tool. Capture any animated web page as an MP4 video. Screenshot animated websites, capture full page animations, and record any webpage online without installing anything.',
        url: 'https://microlink.io/tools/website-screenshot/animated',
        applicationCategory: ['DeveloperApplication', 'Tool'],
        keywords: [
          'animated screenshot',
          'animated screen capture',
          'screenshot website animated',
          'capture animated web page',
          'screen capture animated web page',
          'screenshot web page with animated'
        ],
        offers: {
          '@type': 'Offer',
          price: '0',
          priceCurrency: 'USD',
          description: 'Free tier with 25 animated screenshots per day'
        }
      },
      {
        '@context': 'https://schema.org',
        '@type': 'FAQPage',
        mainEntity: [
          {
            '@type': 'Question',
            name: 'Is this animated screenshot tool really free?',
            acceptedAnswer: {
              '@type': 'Answer',
              text: 'Yes. You can take up to 25 animated screenshots per day at no cost — no credit card, no account required. Every free capture includes all features: device emulation, ad blocking, and CDN caching. Need more? See our pricing plans for higher daily limits and priority processing.'
            }
          },
          {
            '@type': 'Question',
            name: 'How do I take an animated screenshot of a webpage?',
            acceptedAnswer: {
              '@type': 'Answer',
              text: 'Paste the URL into the tool, choose a device (desktop, tablet, or mobile), set the recording duration, and click Generate. We open the page in a real browser, capture the animated web page — including CSS transitions, scroll effects, and interactive states — and return an MP4 video within seconds.'
            }
          },
          {
            '@type': 'Question',
            name: 'How long can an animated screenshot be?',
            acceptedAnswer: {
              '@type': 'Answer',
              text: 'You can record between 2 and 10 seconds. The default is 5 seconds. For pages with delayed animations or slow scroll interactions, increase the duration using the slider in the options panel.'
            }
          },
          {
            '@type': 'Question',
            name: 'Can I take a full page animated screenshot of a long web page?',
            acceptedAnswer: {
              '@type': 'Answer',
              text: 'The tool captures the visible viewport for the selected duration. For long web pages, increase the recording time so more of the page content and animations are captured. On the API, you have full control over duration and viewport to capture entire animated pages programmatically.'
            }
          },
          {
            '@type': 'Question',
            name: 'What format are animated screenshots saved in?',
            acceptedAnswer: {
              '@type': 'Answer',
              text: 'All animated screenshots are delivered as MP4 files using the H.264 codec. MP4 plays natively in every major browser and device, keeping file sizes small without sacrificing quality.'
            }
          }
        ]
      }
    ]}
  />
)

/* ─── Page Component ───────────────────────────────────── */

const AnimatedWebsiteScreenshotPage = () => (
  <Layout>
    <Hero />
    <AnimatedScreenshotTool />
    <HowItWorks />
    <Explanation />
    <UseCases />
    <Banner />
    <Features
      css={theme({ px: 4, py: SECTION_VERTICAL_SPACING })}
      title={
        <Subhead css={{ width: '100%', textAlign: 'left' }}>
          Screenshot API{' '}
          <span
            css={{
              display: 'block',
              color: '#fd494a',
              width: '100%',
              textAlign: 'left'
            }}
          >
            for Automated Screen Capture.
          </span>
        </Subhead>
      }
      caption={
        <>
          No servers to maintain, no headless browsers to manage, no paying for
          capacity you don't use. Add animated screen capture to any app in
          minutes — easy integration via{' '}
          <Link href='/screenshot'>Screenshot API</Link>.
        </>
      }
      features={FEATURES_LIST}
    />
    <ApiDocsCard
      title='Animated Screenshot API documentation'
      description='Integrate animated screen capture into any app. Explore the full API reference with interactive examples, SDKs for every language, and copy-paste code snippets.'
    />
    <ProductInformation />
  </Layout>
)

export default AnimatedWebsiteScreenshotPage
