import { useEffect, useRef, useState } from 'react'
import { api } from '../api'

async function fetchAllRows(source) {
  const res = await api.getViewData(source, 100000, 0)
  return res.rows || []
}

let perspectivePromise = null

function loadPerspective() {
  if (perspectivePromise) return perspectivePromise
  perspectivePromise = (async () => {
    if (!document.getElementById('psp-theme')) {
      const link = document.createElement('link')
      link.id = 'psp-theme'
      link.rel = 'stylesheet'
      link.crossOrigin = 'anonymous'
      link.href = 'https://cdn.jsdelivr.net/npm/@perspective-dev/viewer/dist/css/themes.css'
      document.head.appendChild(link)
    }
    const [{ default: perspective }] = await Promise.all([
      import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@perspective-dev/client@4.4.0/dist/cdn/perspective.js'),
      import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@perspective-dev/viewer@4.4.0/dist/cdn/perspective-viewer.js'),
      import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@perspective-dev/viewer-datagrid@4.4.0/dist/cdn/perspective-viewer-datagrid.js'),
      import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@perspective-dev/viewer-d3fc@4.4.0/dist/cdn/perspective-viewer-d3fc.js'),
    ])
    return perspective
  })()
  return perspectivePromise
}

function formatVal(v) {
  if (v == null) return null
  // Perspective returns dates as ms timestamps
  if (typeof v === 'number' && v > 1e11 && v < 2e12) {
    const d = new Date(v)
    if (!isNaN(d)) return d.toISOString().slice(0, 10)
  }
  return String(v)
}

function normalize(v) {
  if (v == null) return null
  // Perspective returns dates as ms timestamps — convert to ISO date string
  if (typeof v === 'number' && v > 1e11 && v < 2e12) return new Date(v).toISOString().slice(0, 10)
  return String(v).trim()
}

function filterRows(allRows, row, config) {
  const groupBy = config.group_by || []

  if (groupBy.length === 0) {
    // Flat view — clicked row IS the record
    return [row]
  }

  // __ROW_PATH__ is an array of the group_by values in order, e.g. ["Groceries", "Wal-Mart"]
  const rowPath = row['__ROW_PATH__']
  const pathVals = Array.isArray(rowPath)
    ? rowPath
    : String(rowPath).split(',').map(s => s.trim())

  // Zip group_by columns with __ROW_PATH__ values to build filter criteria
  const criteria = groupBy
    .map((col, i) => ({ col, val: pathVals[i] != null ? String(pathVals[i]).trim() : null }))
    .filter(({ val }) => val != null && val !== '')

  if (criteria.length === 0) return allRows

  return allRows.filter(r =>
    criteria.every(({ col, val }) => normalize(r[col]) === val)
  )
}

const LAYOUT_KEY = (source) => `psp_layout_${source}`
const DEFAULT_PLUGIN_CONFIG = { edit_mode: 'SELECT_ROW' }

export default function Pivot({ source }) {
  const viewerRef = useRef()
  const workerRef = useRef()
  const allRowsRef = useRef([])
  const [status, setStatus] = useState('idle')
  const [error, setError] = useState('')
  const [saved, setSaved] = useState(false)
  const [inspectedRows, setInspectedRows] = useState(null)
  const [clickDetail, setClickDetail] = useState(null)

  useEffect(() => {
    if (!source) return
    let cancelled = false
    setSaved(false)
    setInspectedRows(null)
    setClickDetail(null)
    allRowsRef.current = []

    async function init() {
      setStatus('loading')
      setError('')
      try {
        const [perspective, rows] = await Promise.all([
          loadPerspective(),
          fetchAllRows(source),
        ])
        if (cancelled) return
        if (!rows.length) { setStatus('noview'); return }

        allRowsRef.current = rows

        if (workerRef.current) { try { workerRef.current.terminate() } catch {} }

        const worker = await perspective.worker()
        if (cancelled) { worker.terminate(); return }
        workerRef.current = worker

        await worker.table(rows, { name: source })
        if (cancelled) return

        const viewer = viewerRef.current

        viewer.addEventListener('perspective-click', async (e) => {
          const detail = e.detail || {}
          const { row, column_names } = detail
          if (!row) return
          // perspective-click's config only has filter — save() gives us the full config incl. group_by
          const config = await viewer.save()
          setClickDetail({ row, config, column_names })
          const matched = filterRows(allRowsRef.current, row, config)
          setInspectedRows(matched)
        })

        await viewer.load(worker)

        const savedLayout = localStorage.getItem(LAYOUT_KEY(source))
        if (savedLayout) {
          await viewer.restore(JSON.parse(savedLayout))
        } else {
          await viewer.restore({ table: source, settings: true, plugin_config: DEFAULT_PLUGIN_CONFIG })
        }
        setStatus('ready')
      } catch (err) {
        if (!cancelled) { setStatus('error'); setError(err.message) }
      }
    }

    init()
    return () => { cancelled = true }
  }, [source])

  async function saveLayout() {
    const viewer = viewerRef.current
    if (!viewer) return
    const layout = await viewer.save()
    localStorage.setItem(LAYOUT_KEY(source), JSON.stringify(layout))
    setSaved(true)
    setTimeout(() => setSaved(false), 2000)
  }

  function clearLayout() {
    localStorage.removeItem(LAYOUT_KEY(source))
    const viewer = viewerRef.current
    if (viewer) viewer.restore({ table: source, settings: true, plugin_config: DEFAULT_PLUGIN_CONFIG })
  }

  if (!source) return <div className="p-6 text-sm text-gray-400">Select a source first.</div>

  const cols = inspectedRows?.length ? Object.keys(inspectedRows[0]) : []

  return (
    <div className="relative w-full h-full flex">
      <div className="relative flex-1">
        {status === 'ready' && (
          <div className="absolute top-2 right-3 z-20 flex gap-2">
            <button onClick={saveLayout}
              className="text-xs bg-white border border-gray-200 rounded px-2 py-1 text-gray-500 hover:border-gray-400 shadow-sm">
              {saved ? 'Saved!' : 'Save layout'}
            </button>
            {localStorage.getItem(LAYOUT_KEY(source)) && (
              <button onClick={clearLayout}
                className="text-xs bg-white border border-gray-200 rounded px-2 py-1 text-gray-400 hover:text-red-500 shadow-sm">
                Reset
              </button>
            )}
          </div>
        )}
        {status === 'loading' && (
          <div className="absolute inset-0 flex items-center justify-center z-10 bg-gray-50">
            <p className="text-sm text-gray-400">Loading…</p>
          </div>
        )}
        {status === 'error' && (
          <div className="absolute inset-0 flex items-center justify-center z-10 bg-gray-50">
            <p className="text-sm text-red-500">Error: {error}</p>
          </div>
        )}
        {status === 'noview' && (
          <div className="absolute inset-0 flex items-center justify-center z-10 bg-gray-50">
            <p className="text-sm text-gray-400">No view data — generate a view and transform records first.</p>
          </div>
        )}
        <perspective-viewer
          ref={viewerRef}
          style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
        />
      </div>

      {inspectedRows && clickDetail && (
        <div className="w-80 border-l border-gray-200 bg-white flex flex-col overflow-hidden flex-shrink-0">
          <div className="flex items-center justify-between px-3 py-2 border-b border-gray-100">
            <span className="text-xs font-semibold text-gray-600 uppercase tracking-wide">
              {inspectedRows.length} row{inspectedRows.length !== 1 ? 's' : ''}
            </span>
            <button onClick={() => { setInspectedRows(null); setClickDetail(null) }}
              className="text-gray-300 hover:text-gray-500 leading-none text-lg">×</button>
          </div>
          <div className="flex-1 overflow-y-auto">

            {/* Click context — row values from Perspective */}
            <div className="px-3 py-2 border-b border-gray-100">
              <div className="text-xs text-gray-400 uppercase tracking-wide mb-1">
                {[...(clickDetail.config?.group_by || []), ...(clickDetail.config?.split_by || [])].join(' › ') || clickDetail.column_names?.join(', ') || 'Cell'}
              </div>
              {/* Row path */}
              {clickDetail.row['__ROW_PATH__'] && (
                <div className="text-xs text-gray-700 font-mono font-semibold mb-1">
                  {Array.isArray(clickDetail.row['__ROW_PATH__'])
                    ? clickDetail.row['__ROW_PATH__'].join(' › ')
                    : String(clickDetail.row['__ROW_PATH__'])}
                </div>
              )}
              {/* Non-null metric values only */}
              {Object.entries(clickDetail.row)
                .filter(([k, v]) => k !== '__ROW_PATH__' && v != null)
                .map(([k, v]) => (
                  <div key={k} className="flex justify-between py-0.5 gap-2">
                    <span className="text-xs text-gray-400 font-mono shrink-0">{k}</span>
                    <span className="text-xs text-gray-700 font-mono text-right">{formatVal(v)}</span>
                  </div>
                ))}
            </div>

            {/* Config context — group_by / split_by / filters if any */}
            {(clickDetail.config?.group_by?.length > 0 || clickDetail.config?.split_by?.length > 0 || clickDetail.config?.filter?.length > 0) && (
              <div className="px-3 py-2 border-b border-gray-100">
                <div className="text-xs text-gray-400 uppercase tracking-wide mb-1">Pivot context</div>
                {clickDetail.config.group_by?.length > 0 && (
                  <div className="text-xs text-gray-500 py-0.5">group by: <span className="font-mono text-gray-700">{clickDetail.config.group_by.join(', ')}</span></div>
                )}
                {clickDetail.config.split_by?.length > 0 && (
                  <div className="text-xs text-gray-500 py-0.5">split by: <span className="font-mono text-gray-700">{clickDetail.config.split_by.join(', ')}</span></div>
                )}
                {clickDetail.config.filter?.map((f, i) => (
                  <div key={i} className="text-xs text-gray-500 py-0.5 font-mono">{f.join(' ')}</div>
                ))}
              </div>
            )}

            {/* Underlying rows */}
            {inspectedRows.length > 0 && (
              <div className="overflow-auto">
                <table className="w-full text-xs">
                  <thead>
                    <tr className="text-left text-gray-400 border-b border-gray-100 bg-gray-50 sticky top-0">
                      {cols.map(c => (
                        <th key={c} className="px-2 py-1 font-medium whitespace-nowrap">{c}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {inspectedRows.map((row, i) => (
                      <tr key={i} className="border-t border-gray-50 hover:bg-gray-50">
                        {cols.map(c => {
                          const f = formatVal(row[c])
                          return (
                            <td key={c} className="px-2 py-1 font-mono whitespace-nowrap text-gray-700 max-w-40 truncate">
                              {f == null ? <span className="text-gray-300">—</span> : f}
                            </td>
                          )
                        })}
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  )
}
