import React, { useState, useEffect } from "react";
import PlayIcon from "@material-ui/icons/PlayCircleFilled";
import {
  TextInput,
  SelectInput,
  required,
  useTranslate,
  useNotify,
  ReferenceInput,
  ReferenceField,
  TextField,
} from "react-admin";
import { IconButton, CircularProgress } from "@material-ui/core";
import absoluteUrl from "../../../lib/absolute-url";
import TwilioLanguages from "./twilio-languages";

type TTSProvider = (voice: any, language: any, prompt: any) => Promise<void>;

const tts = async (providerId: any): Promise<TTSProvider> => {
  const r = await fetch(
    `/api/v1/voice/twilio/text-to-speech-token/${providerId}`
  );
  const { token } = await r.json();
  const twilioClient = await import("twilio-client");
  return (voice, language, prompt): Promise<void> =>
    new Promise((resolve) => {
      if (!voice || !language || !prompt) resolve();
      const Device = twilioClient.Device;
      const device = new Device();
      const silence = `${absoluteUrl().origin}/static/silence.mp3`;
      device.setup(token, {
        codecPrefences: ["opus", "pcmu"],
        enableRingingState: false,
        fakeLocalDTMF: true,
        disableAudioContextSounds: true,
        sounds: {
          disconnect: silence,
          incoming: silence,
          outgoing: silence,
        },
      });
      device.on("ready", function (device: any) {
        device.connect({ language, voice, prompt });
      });
      device.on("disconnect", () => resolve());
      device.on("error", () => resolve());
    });
};

export const TextToSpeechButton = ({ form }: any) => {
  const { providerId, language, voice, promptText: prompt } = form.formData;
  const [loading, setLoading] = useState<boolean>(false);
  const [ttsProvider, setTTSProvider] = useState<
    undefined | { provider: TTSProvider }
  >(undefined);
  const [playText, setPlayText] = useState<
    undefined | { func: () => Promise<void> }
  >(undefined);
  useEffect(() => {
    (async () => {
      if (providerId) {
        setLoading(true);
        setTTSProvider({ provider: await tts(providerId) });
        setLoading(false);
      }
    })();
  }, [providerId]);

  useEffect(() => {
    (async () => {
      setPlayText({
        func: async () => {
          setLoading(true);
          if (ttsProvider) await ttsProvider.provider(voice, language, prompt);
          setLoading(false);
        },
      });
    })();
  }, [prompt, language, voice, ttsProvider?.provider]);

  const disabled = !(providerId && prompt?.length >= 2 && voice && language);
  /* TODO add this back to IconButtonwhen we know how to extend MUI theme and appease typescript
     variant="contained"
   */
  return (
    <IconButton onClick={playText?.func} disabled={disabled} color="primary">
      {!loading && <PlayIcon />}
      {loading && <CircularProgress size={20} />}
    </IconButton>
  );
};

export const PromptInput = (form: any, ...rest: any[]) => {
  return (
    <TextInput
      source="promptText"
      multiline
      options={{ fullWidth: true }}
      InputProps={{ endAdornment: <TextToSpeechButton form={form} /> }}
      {...rest}
    />
  );
};

const validateVoice = (args: any, values: any) => {
  if (!values.language) return "validation.language";
  if (!values.voice) return "validation.voice";
  // @ts-expect-error
  const availableVoices = TwilioLanguages.voices[values.language];
  const found =
    availableVoices.filter((v: any) => v.id === values.voice).length === 1;
  if (!found) return "validation.voice";

  return undefined;
};

export const VoiceInput = (form: any, ...rest: any[]) => {
  // @ts-expect-error
  const voice = TwilioLanguages.voices[form.formData.language] || [];
  return (
    // @ts-expect-error
    <SelectInput
      source="voice"
      choices={voice}
      validate={[required(), validateVoice]}
      {...rest}
    />
  );
};

let noAvailableNumbers = false;
let availableNumbers: any[] = [];

const getAvailableNumbers = async (providerId: string) => {
  try {
    const r = await fetch(`/api/v1/voice/providers/${providerId}/freeNumbers`);
    availableNumbers = await r.json();
    noAvailableNumbers = availableNumbers.length === 0;
    return availableNumbers;
  } catch (error) {
    console.error(
      `Could not fetch available numbers for provider ${providerId} - ${error}`
    );
    return [];
  }
};

const sidToNumber = (sid: any) => {
  return availableNumbers
    .filter(({ id }) => id === sid)
    .map(({ name }) => name)[0];
};

export const populateNumber = (data: any) => {
  return {
    ...data,
    number: sidToNumber(data.providerLineSid),
  };
};

const hasNumbers = (
  args: any,
  value: any,
  values: any,
  translate: any,
  ...props: any[]
) => {
  if (noAvailableNumbers) return "validation.noAvailableNumbers";

  return undefined;
};

export const AvailableNumbersInput = (form: any, ...rest: any[]) => {
  const {
    // @ts-expect-error: non-existent property
    meta: { touched, error } = {},
    // @ts-expect-error: non-existent property
    input: { ...inputProps },
    ...props
  } = rest;
  const translate = useTranslate();
  const notify = useNotify();
  const [loading, setLoading] = useState(false);
  const [choices, setChoices] = useState({});
  // @ts-expect-error: Invalid return type
  useEffect(async () => {
    if (form && form.formData && form.formData.providerId) {
      setLoading(true);
      const choices = await getAvailableNumbers(form.formData.providerId);
      setChoices({
        choices,
        helperText: noAvailableNumbers
          ? translate("validation.noAvailableNumbers")
          : "",
      });
      if (noAvailableNumbers)
        notify("validation.noAvailableNumbers", { type: "error" });
      setLoading(false);
    }
  }, [form && form.formData ? form.formData.providerId : undefined]);

  return (
    <>
      <SelectInput
        label="Number"
        source="providerLineSid"
        // @ts-expect-error: non-existent property
        choices={choices.choices}
        disabled={loading}
        validate={[hasNumbers, required()]}
        // @ts-expect-error: non-existent property
        error={Boolean(touched && error) || Boolean(choices.helperText)}
        // @ts-expect-error: non-existent property
        helperText={choices.helperText}
        {...inputProps}
        {...props}
      />
      {loading && <CircularProgress />}
    </>
  );
};

/*
const voiceLineName = voiceLine => {
    return voiceLine.number
}
const getVoiceLineChoices = async ():Promise<any[]> => {
  try {
    const r = await fetch(`/api/v1/voice/voice-line`);
    const lines = await r.json();
      if(lines.data?.length > 0) {
          return lines.data.map(voiceLine => ({"id": voiceLine.id, "name": voiceLineName(voiceLine)}))
      }
      return [];
  } catch (error) {
    console.error(
      `Could not fetch voice lines error: ${error}`
    );
    return [];
  }
}

export const AsyncSelectInput = (choiceLoader: () => Promise<any[]>, label, source, translationEmpty,) => (form, ...rest) => {
     const {
        meta: { touched, error } = {},
        input: { ...inputProps },
        ...props
    } = rest;

    const translate = useTranslate();
    const notify = useNotify();
    const [loading, setLoading] = useState(false);
    const [choices, setChoices] = useState({choices: []});
    useEffect(() => {
        (async () => {
            setLoading(true);
            //const items = await choiceLoader()
            const items = [{"id": "testing", "name": "OMG"}]
            setChoices({
                choices: items,
                helperText: items.length === 0
                          ? translate(translationEmpty)
                          : "",
            });
            if (items.length === 0) notify(translationEmpty, "error");
            setLoading(false);
        })()}, [form && form.formData ? form.formData.providerId : undefined]);

    const isNotEmpty = () => {
        if (choices.choices.length === 0) return translationEmpty;
        return undefined;
    };
    return (
        <>
            {choices.choices.length > 0 &&
            <SelectInput
                label={label}
                source={source}
                choices={choices.choices}
                disabled={loading}
                validate={[isNotEmpty, required()]}
                error={Boolean(touched && error) || Boolean(choices.helperText)}
                helperText={choices.helperText}
                {...inputProps}
                {...props}
            />}
            {loading && <CircularProgress />}
        </>
    )
}
export const VoiceLineSelectInput = AsyncSelectInput(getVoiceLineChoices, "Voice Line", "backendId", "validation.noVoiceLines" )
*/

export const VoiceLineSelectInput = (source: string) => () =>
  (
    <ReferenceInput
      label="Voice Line"
      source={source}
      reference="voiceLines"
      validate={[required()]}
    >
      <SelectInput optionText="number" />
    </ReferenceInput>
  );

export const VoiceLineField = (source: string) => () =>
  (
    <ReferenceField label="Voice Line" source={source} reference="voiceLines">
      <TextField source="number" />
    </ReferenceField>
  );
