import React, { useEffect, useState } from 'react'
import API from '../API'
import LeaderboardFilter from './LeaderboardFilter'

interface LeaderboardTableProps {
  file: string
}

interface Row {
  metric: string
  [key: string]: string | number
}

interface Groups {
  [group: string]: { [subgroup: string]: string[] }
}

const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ file }) => {
  const [tableRows, setTableRows] = useState<Row[]>([])
  const [tableHeader, setTableHeader] = useState<string[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [groups, setGroups] = useState<Groups>({})

  const [selectedMetrics, setSelectedMetrics] = useState<Set<string>>(new Set())
  const [defaultSelectedMetrics, setDefaultSelectedMetrics] = useState<string[]>([])

  useEffect(() => {
    API.fetchStaticFile(`data/${file}_benchmark.csv`)
      .then((response) => {
        const data = JSON.parse(response)
        const rows: Row[] = data['rows']
        const groups = data['groups'] as { [key: string]: string[] }

        // Each value of groups is a list of metrics, group them by the first part of the metric before the first _
        const groupsData = Object.entries(groups)
          .sort(([groupA], [groupB]) => {
            // Make sure "overall" comes first
            if (groupA === 'Overall') return -1
            if (groupB === 'Overall') return 1
            // Otherwise sort alphabetically
            return groupA.localeCompare(groupB)
          })
          .reduce(
            (acc, [group, metrics]) => {
              // Sort metrics to ensure consistent subgroup order
              const sortedMetrics = [...metrics].sort()

              // Create and sort subgroups
              acc[group] = sortedMetrics.reduce<{ [key: string]: string[] }>((subAcc, metric) => {
                const [mainGroup, subGroup] = metric.split('_')
                if (!subAcc[mainGroup]) {
                  subAcc[mainGroup] = []
                }
                subAcc[mainGroup].push(metric)
                return subAcc
              }, {})

              // Convert to sorted entries and back to object
              acc[group] = Object.fromEntries(
                Object.entries(acc[group]).sort(([subGroupA], [subGroupB]) =>
                  subGroupA.localeCompare(subGroupB)
                )
              )

              return acc
            },
            {} as { [key: string]: { [key: string]: string[] } }
          )

        const allKeys: string[] = Array.from(new Set(rows.flatMap((row) => Object.keys(row))))
        setSelectedMetrics(new Set(data['default_selected_metrics']))
        setDefaultSelectedMetrics(data['default_selected_metrics'])
        setTableHeader(allKeys)
        setTableRows(rows)
        setGroups(groupsData)
        setLoading(false)
      })
      .catch((err) => {
        setError('Failed to fetch JSON: ' + err.message)
        setLoading(false)
      })
  }, [file])

  const handleSelectDefaults = () => {
    setSelectedMetrics(new Set(defaultSelectedMetrics))
  }

  return (
    <div className="rounded shadow overflow-auto">
      <h3 className="font-bold mb-2">{file}</h3>
      {loading && <div>Loading...</div>}
      {error && <div className="text-red-500">{error}</div>}

      {!loading && !error && (
        <div className="overflow-x-auto">
          <LeaderboardFilter
            groups={groups}
            selectedMetrics={selectedMetrics}
            setSelectedMetrics={setSelectedMetrics}
            defaultSelectedMetrics={defaultSelectedMetrics}
          />
          <table className="table">
            <thead>
              <tr>
                {tableHeader.map((col, idx) => (
                  <th key={idx}>{col}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {tableRows
                .filter((row) => selectedMetrics.has(row['metric'] as string))
                .map((row, i) => (
                  <tr key={i}>
                    {Object.keys(row).map((column, j) => {
                      const cell = row[column]

                      return (
                        <td key={j}>
                          <div className="">
                            {isNaN(Number(cell)) ? cell : Number(Number(cell).toFixed(3))}
                          </div>
                        </td>
                      )
                    })}
                  </tr>
                ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  )
}

export default LeaderboardTable
