import React from 'react';
import { Button, Label, FormGroup } from '@trussworks/react-uswds';
import { FileInput } from 'components';
import { useAuthContext } from 'context';
import Papa from 'papaparse';
import * as FileSaver from 'file-saver';

interface ImportProps<T> {
  // Plural name of the model.
  name: string;

  // Callback that handles data on import (usually saves the data).
  onImport: (e: T[]) => void;
}

export interface ExportProps<T> {
  // Plural name of the model.
  name: string;

  // List of fields to export.
  fieldsToExport?: string[];

  // Return data to be exported.
  getDataToExport: () => Partial<T>[] | Promise<Partial<T>[]> | Promise<string>;
}

interface ImportExportProps<T> extends ImportProps<T>, ExportProps<T> {}

export const Import = <T extends object>(props: ImportProps<T>) => {
  const { setLoading } = useAuthContext();
  const { name, onImport } = props;
  const parseCSV = async (event: React.ChangeEvent<HTMLInputElement>) => {
    if (!event.target.files || !event.target.files.length) {
      return;
    }
    setLoading((l) => l + 1);
    const results: T[] = await new Promise((resolve, reject) =>
      Papa.parse(event.target.files![0], {
        header: true,
        dynamicTyping: true,
        complete: ({ data, errors }) =>
          errors.length ? reject(errors) : resolve(data as T[])
      })
    );
    setLoading((l) => l - 1);
    onImport(results);
  };
  return (
    <form>
      <h2>Import {name}</h2>
      <FormGroup>
        <Label htmlFor="import">
          File must be in a CSV format, with the same header as the exported
          file.
        </Label>
        <FileInput id="import" accept=".csv" onChange={(e) => parseCSV(e)} />
      </FormGroup>
    </form>
  );
};

export const exportCSV = async <T extends object>(
  props: ExportProps<T>,
  setLoading: React.Dispatch<React.SetStateAction<number>>
) => {
  const filename = `${props.name}-${new Date().toISOString()}`;
  setLoading((l) => l + 1);
  const data = await props.getDataToExport();
  if (typeof data === 'string') {
    setLoading((l) => l - 1);
    window.open(data);
    return;
  }
  const csv = Papa.unparse({
    fields: props.fieldsToExport ?? [],
    data: data
  });
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
  FileSaver.saveAs(blob, `${filename}.csv`);
  setLoading((l) => l - 1);
};

export const Export = <T extends object>(props: ExportProps<T>) => {
  const { setLoading } = useAuthContext();
  return (
    <form>
      <h2>Export {props.name}</h2>
      <Button
        type="button"
        outline
        onClick={() => exportCSV(props, setLoading)}
      >
        Export as CSV
      </Button>
    </form>
  );
};

export const ImportExport = <T extends object>(props: ImportExportProps<T>) => {
  const { name, onImport, getDataToExport, fieldsToExport } = props;
  return (
    <>
      <Import name={name} onImport={onImport} />
      <Export
        name={name}
        fieldsToExport={fieldsToExport}
        getDataToExport={getDataToExport}
      />
    </>
  );
};