/* @flow strict-local */
import React, { PureComponent } from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import type { DocumentPickerResponse } from 'react-native-document-picker';
import * as ImagePicker from 'react-native-image-picker';

import type { Dispatch, Narrow } from '../types';
import { connect } from '../react-redux';
import { showErrorAlert } from '../utils/info';
import { BRAND_COLOR } from '../styles';
import {
  IconPlusCircle,
  IconLeft,
  IconPeople,
  IconImage,
  IconCamera,
  IconFile,
} from '../common/Icons';
import AnimatedComponent from '../animation/AnimatedComponent';
import { navigateToCreateGroup, uploadFile } from '../actions';

type Props = $ReadOnly<{|
  dispatch: Dispatch,
  expanded: boolean,
  destinationNarrow: Narrow,
  onExpandContract: () => void,
|}>;

/**
 * Adjust `fileName` to one with the right extension for the file format.
 *
 * Sometimes we get an image whose filename reflects one format (what it's
 * stored as in the camera roll), but the actual image has been converted
 * already to another format for interoperability.
 *
 * The Zulip server will infer the file format from the filename's
 * extension, so in this case we need to adjust the extension to match the
 * actual format.  The clue we get in the image-picker response is the extension
 * found in `uri`.
 *
 * Also if `fileName` is null or undefined, default to the last component of `uri`.
 */
export const chooseUploadImageFilename = (uri: string, fileName: ?string): string => {
  const name = fileName ?? uri.replace(/.*\//, '');

  /*
   * Photos in an iPhone's camera roll (taken since iOS 11) are typically in
   * HEIF format and have file names with the extension `.HEIC`.  When the user
   * selects one of these photos through the image picker, the file gets
   * automatically converted to JPEG format... but the `fileName` property in
   * the react-native-image-picker response still has the `.HEIC` extension.
   */
  if (/\.jpe?g$/i.test(uri)) {
    return name.replace(/\.heic$/i, '.jpeg');
  }

  return name;
};

class ComposeMenu extends PureComponent<Props> {
  uploadFile = (uri: string, fileName: ?string) => {
    const { dispatch, destinationNarrow } = this.props;
    dispatch(uploadFile(destinationNarrow, uri, chooseUploadImageFilename(uri, fileName)));
  };

  handleImagePickerResponse = (
    response: $ReadOnly<{
      didCancel: boolean,
      // Upstream docs are vague:
      // https://github.com/react-native-community/react-native-image-picker/blob/master/docs/Reference.md
      error?: string | void | null | false,
      uri: string,
      // Upstream docs are wrong (fileName may indeed be null, at least on iOS);
      // surfaced in https://github.com/zulip/zulip-mobile/issues/3813:
      // https://github.com/react-native-community/react-native-image-picker/issues/1271
      fileName: ?string,
    }>,
  ) => {
    if (response.didCancel) {
      return;
    }

    // $FlowFixMe sketchy falsiness check, because upstream API is unclear
    const error: string | null = response.error || null;
    if (error !== null) {
      showErrorAlert(error, 'Error');
      return;
    }

    this.uploadFile(response.uri, response.fileName);
  };

  handleImagePicker = () => {
    ImagePicker.launchImageLibrary(
      {
        quality: 1.0,
        noData: true,
        storageOptions: {
          skipBackup: true,
          path: 'images',
        },
      },
      this.handleImagePickerResponse,
    );
  };

  handleCameraCapture = () => {
    const options = {
      storageOptions: {
        cameraRoll: true,
        waitUntilSaved: true,
      },
    };

    ImagePicker.launchCamera(options, this.handleImagePickerResponse);
  };

  handleFilePicker = async () => {
    // Defer import to here, to avoid an obnoxious import-time warning
    // from this library when in the test environment.
    const DocumentPicker = (await import('react-native-document-picker')).default;

    let response;
    try {
      response = (await DocumentPicker.pick({
        type: [DocumentPicker.types.allFiles],
      }): DocumentPickerResponse);
    } catch (e) {
      if (!DocumentPicker.isCancel(e)) {
        showErrorAlert(e, 'Error');
      }
      return;
    }

    this.uploadFile(response.uri, response.name);
  };

  styles = StyleSheet.create({
    composeMenu: {
      flexDirection: 'row',
      overflow: 'hidden',
    },
    expandButton: {
      padding: 12,
      color: BRAND_COLOR,
    },
    composeMenuButton: {
      padding: 12,
      marginRight: -8,
      color: BRAND_COLOR,
    },
  });

  render() {
    const { dispatch, expanded, onExpandContract } = this.props;
    const numIcons = Platform.OS === 'android' ? 4 : 3;
    return (
      <View style={this.styles.composeMenu}>
        <AnimatedComponent
          stylePropertyName="width"
          fullValue={40 * numIcons}
          useNativeDriver={false}
          visible={expanded}
        >
          <View style={this.styles.composeMenu}>
            <IconPeople
              style={this.styles.composeMenuButton}
              size={24}
              onPress={() => {
                dispatch(navigateToCreateGroup());
              }}
            />
            {Platform.OS === 'android' && (
              <IconFile
                style={this.styles.composeMenuButton}
                size={24}
                onPress={this.handleFilePicker}
              />
            )}
            <IconImage
              style={this.styles.composeMenuButton}
              size={24}
              onPress={this.handleImagePicker}
            />
            <IconCamera
              style={this.styles.composeMenuButton}
              size={24}
              onPress={this.handleCameraCapture}
            />
          </View>
        </AnimatedComponent>
        {!expanded && (
          <IconPlusCircle style={this.styles.expandButton} size={24} onPress={onExpandContract} />
        )}
        {expanded && (
          <IconLeft style={this.styles.expandButton} size={24} onPress={onExpandContract} />
        )}
      </View>
    );
  }
}

export default connect<{||}, _, _>()(ComposeMenu);
