import { createElement } from 'react';
import {
  AreaChart,
  BarChart,
  BarGroupChart,
  BarStackChart,
  CalendarChart,
  Choropleth,
  LineChart,
  ParetoChart,
  PieChart,
  RadarChart,
  ScatterChart,
  TreeMap,
} from 'infografika';

import { ErrorBoundary } from 'components/ErrorBoundary';
import { Tile } from './Tile';
import { MapFeaturesProvider } from './MapFeaturesProvider';

const ChoroplethChart = ({ region, ...props }: { region: string }) => {
  return (
    <MapFeaturesProvider region={region}>
      {(features: any) => <Choropleth {...props} features={features} />}
    </MapFeaturesProvider>
  );
};

type ChartsConfig = {
  component: React.ElementType;
  defaultProps?: Record<string, any>;
};

const CHARTS: Record<string, ChartsConfig> = {
  AreaChart: { component: AreaChart },
  BarChart: { component: BarChart },
  CalendarChart: { component: CalendarChart },
  Choropleth: { component: ChoroplethChart },
  GroupChart: { component: BarGroupChart },
  ParetoChart: { component: ParetoChart },
  PieChart: { component: PieChart },
  LineChart: { component: LineChart },
  RadarChart: { component: RadarChart },
  ScatterChart: { component: ScatterChart },
  StackChart: { component: BarStackChart },
  TreeMap: { component: TreeMap },
};

const ChartFallback = ({ children }: { children: React.ReactNode }) => {
  return (
    <div className="flex alignCenter justifyCenter w100 h100 c-gray">
      {children}
    </div>
  );
};

interface ChartTileProps {
  id?: string;
  type: string;
  label: string;
  description?: string;
  data: any;
  options?: Record<string, any>;
  defaultProps?: Record<string, any>;
}

export const ChartTile = ({ defaultProps, ...content }: ChartTileProps) => {
  const { id, type, label, description, data, options, ...props } = content;

  const hasData = Array.isArray(data) && data.length;

  const config = CHARTS[type];

  if (config.component === undefined) {
    throw new Error(`Unknown type of chart: ${type}`);
  }

  return (
    <Tile name={label} description={description}>
      {hasData ? (
        <ErrorBoundary
          fallbackRender={(error) => {
            return <ChartFallback>{error.message}</ChartFallback>;
          }}
        >
          {createElement(config.component, {
            data,
            ...config.defaultProps,
            ...options,
            ...props,
          })}
        </ErrorBoundary>
      ) : (
        <ChartFallback>Nothing to visualize</ChartFallback>
      )}
    </Tile>
  );
};
