import type { Ref } from 'react'
import type { DownloadItem } from './types'
import html2canvas from 'html2canvas'
import { ImageOutlined } from '@mui/icons-material'
import { SvgIcon } from '@mui/material'

// Helper to escape CSV cell values
function escapeCSVCell<D>(value: D): string {
  const str =
    value == null
      ? ''
      : typeof value === 'object'
        ? JSON.stringify(value)
        : String(value)

  // If the value contains a comma, quote, or newline, wrap in quotes and escape quotes
  if (/[",\n]/.test(str)) {
    return `"${str.replace(/"/g, '""')}"`
  }
  return str
}

async function downloadFileToCSV<D>(data: D[][]) {
  const csvContent = data
    .map((row) => row.map(escapeCSVCell<D>).join(','))
    .join('\n')
  const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
  return Promise.resolve(URL.createObjectURL(blob))
}

/**
 * Pre-configured download item for exporting widget data as a CSV file.
 *
 * Converts a 2D array of data into CSV format with proper escaping and
 * triggers a browser download. Revokes the object URL after download.
 */
export const downloadToCSV: DownloadItem<unknown[][]> = {
  id: 'csv',
  label: 'CSV',
  icon: (
    <SvgIcon>
      <svg
        xmlns='http://www.w3.org/2000/svg'
        width='18'
        height='18'
        fill='none'
        viewBox='0 0 18 18'
      >
        <path
          fill='currentColor'
          d='M4.313 11.25h2.25v-1.125H4.688v-2.25h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v3c0 .213.072.39.216.534a.726.726 0 0 0 .534.216Zm2.925 0h2.25c.212 0 .39-.072.534-.216a.726.726 0 0 0 .216-.534V9.375a.931.931 0 0 0-.216-.59.658.658 0 0 0-.534-.273H8.363v-.637h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v1.125c0 .213.072.403.216.572a.675.675 0 0 0 .534.253h1.126v.675H7.238v1.125Zm4.95 0h1.124l1.313-4.5H13.5l-.75 2.588L12 6.75h-1.125l1.313 4.5ZM3 15c-.413 0-.766-.147-1.06-.44a1.445 1.445 0 0 1-.44-1.06v-9c0-.412.147-.766.44-1.06C2.235 3.148 2.588 3 3 3h12c.412 0 .766.147 1.06.44.293.294.44.648.44 1.06v9c0 .412-.147.766-.44 1.06-.294.293-.647.44-1.06.44H3Zm0-1.5h12v-9H3v9Z'
        />
      </svg>
    </SvgIcon>
  ),
  modifier: downloadFileToCSV,
  callback: (data) => {
    URL.revokeObjectURL(data)
  },
}

async function downloadFileToPNG(ref: Ref<HTMLElement | null> | undefined) {
  if (!ref || typeof ref === 'function' || !ref.current) {
    // eslint-disable-next-line no-console
    console.warn(
      '[CARTO downloadFileToPNG] Invalid ref passed to downloadFileToPNG. ' +
        'Expected a React ref object with a .current property pointing to an HTMLElement. ' +
        'Download aborted.',
    )
    return
  }

  const element = ref.current
  const clone = element.cloneNode(true) as HTMLElement

  clone.querySelector('.widget-toolbar-container')?.remove()
  clone.querySelector('.widget-wrapper-actions')?.remove()

  document.body.appendChild(clone)

  const rect = clone.getBoundingClientRect()

  const opts = {
    useCORS: true,
    scale: 2,
    backgroundColor: '#fff',
    width: rect.width,
    height: rect.height,
  }

  const canvasResult: HTMLCanvasElement = await html2canvas(clone, opts)

  const result = canvasResult.toDataURL('image/png')

  document.body.removeChild(clone)

  return Promise.resolve(result)
}

/**
 * Pre-configured download item for exporting a widget as a PNG image.
 *
 * Uses html2canvas to capture the widget DOM element referenced by a React ref.
 * Strips toolbar and action elements before capturing.
 *
 * @remarks
 * The modifier expects a React ref to the widget's root HTML element, not raw data.
 */
export const downloadToPNG: Omit<DownloadItem, 'modifier'> & {
  modifier: (
    ref: Ref<HTMLElement | null> | undefined,
  ) => Promise<string | undefined>
} = {
  id: 'png',
  label: 'PNG',
  icon: <ImageOutlined />,
  modifier: downloadFileToPNG,
}
