// @flow

// $FlowFixMe
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import React from 'react';
import classnames from 'classnames';
import Button from 'component/button';
import TagsSearch from 'component/tagsSearch';
import ErrorText from 'component/common/error-text';
import ClaimAbandonButton from 'component/claimAbandonButton';
import ChannelSelector from 'component/channelSelector';
import ClaimList from 'component/claimList';
import Card from 'component/common/card';
import LbcSymbol from 'component/common/lbc-symbol';
import SelectThumbnail from 'component/selectThumbnail';
import { useHistory } from 'react-router-dom';
import { isNameValid, regexInvalidURI } from 'util/lbryURI';
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
import { FormField } from 'component/common/form';
import { handleBidChange } from 'util/publish';
import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field';
import { INVALID_NAME_ERROR } from 'constants/claim';
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
import * as PAGES from 'constants/pages';
import analytics from 'analytics';

const LANG_NONE = 'none';
const MAX_TAG_SELECT = 5;

type Props = {
  uri: string,
  claim: CollectionClaim,
  balance: number,
  disabled: boolean,
  activeChannelClaim: ?ChannelClaim,
  incognito: boolean,
  // params
  title: string,
  amount: number,
  thumbnailUrl: string,
  description: string,
  tags: Array<string>,
  locations: Array<string>,
  languages: Array<string>,
  collectionId: string,
  collection: Collection,
  collectionClaimIds: Array<string>,
  collectionUrls: Array<string>,
  updatingCollection: boolean,
  updateError: string,
  createError: string,
  creatingCollection: boolean,
  publishCollectionUpdate: (CollectionUpdateParams) => Promise<any>,
  publishCollection: (CollectionPublishParams, string) => Promise<any>,
  clearCollectionErrors: () => void,
  onDone: (string) => void,
  setActiveChannel: (string) => void,
  setIncognito: (boolean) => void,
  doCollectionEdit: (CollectionEditParams) => void,
  resetThumbnailStatus: () => void,
};

function CollectionForm(props: Props) {
  const {
    uri, // collection uri
    claim,
    balance,
    // publish params
    amount,
    title,
    description,
    thumbnailUrl,
    tags,
    locations,
    languages = [],
    // rest
    updateError,
    updatingCollection,
    creatingCollection,
    createError,
    disabled,
    activeChannelClaim,
    incognito,
    collectionId,
    collection,
    collectionUrls,
    collectionClaimIds,
    publishCollectionUpdate,
    publishCollection,
    clearCollectionErrors,
    setActiveChannel,
    setIncognito,
    onDone,
    doCollectionEdit,
    resetThumbnailStatus,
  } = props;
  const activeChannelName = activeChannelClaim && activeChannelClaim.name;
  let prefix = 'lbry://';
  if (activeChannelName && !incognito) {
    prefix += `${activeChannelName}/`;
  }
  const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id;
  const collectionName = (claim && claim.name) || (collection && collection.name);
  const collectionChannel = claim && claim.signing_channel ? claim.signing_channel.claim_id : undefined;
  const hasClaim = !!claim;
  const [initialized, setInitialized] = React.useState(false);
  const [nameError, setNameError] = React.useState(undefined);
  const [bidError, setBidError] = React.useState('');
  const [thumbStatus, setThumbStatus] = React.useState('');
  const [thumbError, setThumbError] = React.useState('');
  const [params, setParams]: [any, (any) => void] = React.useState({});
  const name = params.name;
  const isNewCollection = !uri;
  const { replace } = useHistory();
  const languageParam = params.languages || [];
  const primaryLanguage = Array.isArray(languageParam) && languageParam.length && languageParam[0];
  const secondaryLanguage = Array.isArray(languageParam) && languageParam.length >= 2 && languageParam[1];
  const hasClaims = params.claims && params.claims.length;
  const collectionClaimIdsString = JSON.stringify(collectionClaimIds);
  const itemError = !hasClaims ? __('Cannot publish empty list') : '';
  const thumbnailError =
    (thumbError && thumbStatus !== THUMBNAIL_STATUSES.COMPLETE && __('Invalid thumbnail')) ||
    (thumbStatus === THUMBNAIL_STATUSES.IN_PROGRESS && __('Please wait for thumbnail to finish uploading'));
  const submitError = nameError || bidError || itemError || updateError || createError || thumbnailError;

  function parseName(newName) {
    let INVALID_URI_CHARS = new RegExp(regexInvalidURI, 'gu');
    return newName.replace(INVALID_URI_CHARS, '-');
  }

  function setParam(paramObj) {
    setParams({ ...params, ...paramObj });
  }

  function updateParams(paramsObj) {
    setParams({ ...params, ...paramsObj });
  }

  // TODO remove this or better decide whether app should delete languages[2+]
  // This was added because a previous update setting was duplicating language codes
  function dedupeLanguages(languages) {
    if (languages.length <= 1) {
      return languages;
    } else if (languages.length === 2) {
      if (languages[0] !== languages[1]) {
        return languages;
      } else {
        return [languages[0]];
      }
    } else if (languages.length > 2) {
      const newLangs = [];
      languages.forEach((l) => {
        if (!newLangs.includes(l)) {
          newLangs.push(l);
        }
      });
      return newLangs;
    }
  }

  function handleUpdateThumbnail(update: { [string]: string }) {
    if (update.thumbnail_url !== undefined) {
      setParam(update);
    } else if (update.thumbnail_status) {
      setThumbStatus(update.thumbnail_status);
    } else {
      setThumbError(update.thumbnail_error);
    }
  }

  function getCollectionParams() {
    const collectionParams: {
      thumbnail_url?: string,
      name?: string,
      description?: string,
      title?: string,
      bid: string,
      languages?: ?Array<string>,
      locations?: ?Array<string>,
      tags?: ?Array<{ name: string }>,
      claim_id?: string,
      channel_id?: string,
      claims: ?Array<string>,
    } = {
      thumbnail_url: thumbnailUrl,
      name: parseName(collectionName),
      description,
      title: claim ? title : collectionName,
      bid: String(amount || 0.001),
      languages: languages ? dedupeLanguages(languages) : [],
      locations: locations || [],
      tags: tags
        ? tags.map((tag) => {
            return { name: tag };
          })
        : [],
      claim_id: claim ? claim.claim_id : undefined,
      channel_id: claim ? collectionChannel : activeChannelId || undefined,
      claims: collectionClaimIds,
    };

    return collectionParams;
  }

  function handleOnDragEnd(result) {
    const { source, destination } = result;

    if (!destination) return;

    const { index: from } = source;
    const { index: to } = destination;

    doCollectionEdit({ order: { from, to } });
  }

  function handleLanguageChange(index, code) {
    let langs = [...languageParam];
    if (index === 0) {
      if (code === LANG_NONE) {
        // clear all
        langs = [];
      } else {
        langs[0] = code;
        if (langs[0] === langs[1]) {
          langs.length = 1;
        }
      }
    } else {
      if (code === LANG_NONE || code === langs[0]) {
        langs.splice(1, 1);
      } else {
        langs[index] = code;
      }
    }
    setParams({ ...params, languages: langs });
  }

  function handleSubmit() {
    if (uri) {
      publishCollectionUpdate(params).then((pendingClaim) => {
        if (pendingClaim) {
          const claimId = pendingClaim.claim_id;
          analytics.apiLogPublish(pendingClaim);
          onDone(claimId);
        }
      });
    } else {
      publishCollection(params, collectionId).then((pendingClaim) => {
        if (pendingClaim) {
          const claimId = pendingClaim.claim_id;
          analytics.apiLogPublish(pendingClaim);
          onDone(claimId);
        }
      });
    }
  }

  React.useEffect(() => {
    const collectionClaimIds = JSON.parse(collectionClaimIdsString);
    setParams({ ...params, claims: collectionClaimIds });
    clearCollectionErrors();
  }, [collectionClaimIdsString, setParams]);

  React.useEffect(() => {
    let nameError;
    if (!name && name !== undefined) {
      nameError = __('A name is required for your url');
    } else if (!isNameValid(name)) {
      nameError = INVALID_NAME_ERROR;
    }

    setNameError(nameError);
  }, [name]);

  // on mount, if we get a collectionChannel, set it.
  React.useEffect(() => {
    if (!initialized) {
      if (hasClaim) {
        if (collectionChannel) {
          setActiveChannel(collectionChannel);
          setIncognito(false);
        } else if (!collectionChannel && hasClaim) {
          setIncognito(true);
        }
      } else {
        if (incognito) {
          setIncognito(true);
        }
      }
      setInitialized(true);
    }
  }, [setInitialized, setActiveChannel, collectionChannel, setIncognito, hasClaim, incognito, initialized]);

  // every time activechannel or incognito changes, set it.
  React.useEffect(() => {
    if (initialized) {
      if (activeChannelId && !incognito) {
        setParam({ channel_id: activeChannelId });
      }
      if (incognito) {
        setParam({ channel_id: undefined });
      }
    }
  }, [activeChannelId, incognito, initialized]);

  // setup initial params after we're sure if it's published or not
  React.useEffect(() => {
    if (!uri || (uri && hasClaim)) {
      updateParams(getCollectionParams());
    }
  }, [uri, hasClaim]);

  React.useEffect(() => {
    resetThumbnailStatus();
  }, [resetThumbnailStatus]);

  return (
    <>
      <div className={classnames('main--contained', { 'card--disabled': disabled })}>
        <Tabs>
          <TabList className="tabs__list--collection-edit-page">
            <Tab>{__('General')}</Tab>
            <Tab>{__('Items')}</Tab>
            <Tab>{__('Credits')}</Tab>
            <Tab>{__('Tags')}</Tab>
            <Tab>{__('Other')}</Tab>
          </TabList>
          <TabPanels>
            <TabPanel>
              <div className={'card-stack'}>
                <ChannelSelector disabled={disabled} />
                <Card
                  body={
                    <>
                      <fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix">
                        <fieldset-section>
                          <label htmlFor="channel_name">{__('Name')}</label>
                          <div className="form-field__prefix">{prefix}</div>
                        </fieldset-section>

                        <FormField
                          autoFocus={isNewCollection}
                          type="text"
                          name="channel_name"
                          placeholder={__('MyAwesomeList')}
                          value={params.name}
                          error={nameError}
                          disabled={!isNewCollection}
                          onChange={(e) => setParams({ ...params, name: e.target.value || '' })}
                        />
                      </fieldset-group>
                      {!isNewCollection && (
                        <span className="form-field__help">{__('This field cannot be changed.')}</span>
                      )}

                      <FormField
                        type="text"
                        name="channel_title2"
                        label={__('Title')}
                        placeholder={__('My Awesome List')}
                        value={params.title}
                        onChange={(e) => setParams({ ...params, title: e.target.value })}
                      />
                      <fieldset-section>
                        <SelectThumbnail
                          thumbnail={params.thumbnail_url}
                          thumbnailError={thumbError}
                          thumbnailParamStatus={thumbStatus}
                          updateThumbnailParams={handleUpdateThumbnail}
                          usePublishFormMode
                        />
                      </fieldset-section>
                      <FormField
                        type="markdown"
                        name="content_description2"
                        label={__('Description')}
                        placeholder={__('Description of your content')}
                        value={params.description}
                        onChange={(text) => setParams({ ...params, description: text })}
                        textAreaMaxLength={FF_MAX_CHARS_IN_DESCRIPTION}
                      />
                    </>
                  }
                />
              </div>
            </TabPanel>
            <TabPanel>
              <DragDropContext onDragEnd={handleOnDragEnd}>
                <Droppable droppableId="list__ordering">
                  {(DroppableProvided) => (
                    <ClaimList
                      uris={collectionUrls}
                      collectionId={collectionId}
                      empty={__('This list has no items.')}
                      showEdit
                      droppableProvided={DroppableProvided}
                    />
                  )}
                </Droppable>
              </DragDropContext>
            </TabPanel>
            <TabPanel>
              <Card
                body={
                  <FormField
                    className="form-field--price-amount"
                    type="number"
                    name="content_bid2"
                    step="any"
                    label={<LbcSymbol postfix={__('Deposit')} size={14} />}
                    value={params.bid}
                    error={bidError}
                    min="0.0"
                    disabled={false}
                    onChange={(event) =>
                      handleBidChange(parseFloat(event.target.value), amount, balance, setBidError, setParam)
                    }
                    placeholder={0.1}
                    helper={__('Increasing your deposit can help your channel be discovered more easily.')}
                  />
                }
              />
            </TabPanel>
            <TabPanel>
              <Card
                body={
                  <TagsSearch
                    suggestMature
                    disableAutoFocus
                    limitSelect={MAX_TAG_SELECT}
                    tagsPassedIn={params.tags || []}
                    label={__('Selected Tags')}
                    onRemove={(clickedTag) => {
                      const newTags = params.tags.slice().filter((tag) => tag.name !== clickedTag.name);
                      setParams({ ...params, tags: newTags });
                    }}
                    onSelect={(newTags) => {
                      newTags.forEach((newTag) => {
                        if (!params.tags.map((savedTag) => savedTag.name).includes(newTag.name)) {
                          setParams({ ...params, tags: [...params.tags, newTag] });
                        } else {
                          // If it already exists and the user types it in, remove it
                          setParams({ ...params, tags: params.tags.filter((tag) => tag.name !== newTag.name) });
                        }
                      });
                    }}
                  />
                }
              />
            </TabPanel>
            <TabPanel>
              <Card
                body={
                  <>
                    <FormField
                      name="language_select"
                      type="select"
                      label={__('Primary Language')}
                      onChange={(event) => handleLanguageChange(0, event.target.value)}
                      value={primaryLanguage}
                      helper={__('Your main content language')}
                    >
                      <option key={'pri-langNone'} value={LANG_NONE}>
                        {__('None selected')}
                      </option>
                      {Object.keys(SUPPORTED_LANGUAGES).map((language) => (
                        <option key={language} value={language}>
                          {SUPPORTED_LANGUAGES[language]}
                        </option>
                      ))}
                    </FormField>
                    <FormField
                      name="language_select2"
                      type="select"
                      label={__('Secondary Language')}
                      onChange={(event) => handleLanguageChange(1, event.target.value)}
                      value={secondaryLanguage}
                      disabled={!languageParam[0]}
                      helper={__('Your other content language')}
                    >
                      <option key={'sec-langNone'} value={LANG_NONE}>
                        {__('None selected')}
                      </option>
                      {Object.keys(SUPPORTED_LANGUAGES)
                        .filter((lang) => lang !== languageParam[0])
                        .map((language) => (
                          <option key={language} value={language}>
                            {SUPPORTED_LANGUAGES[language]}
                          </option>
                        ))}
                    </FormField>
                  </>
                }
              />
            </TabPanel>
          </TabPanels>
        </Tabs>
        <Card
          className="card--after-tabs"
          actions={
            <>
              <div className="section__actions">
                <Button
                  button="primary"
                  disabled={
                    creatingCollection || updatingCollection || nameError || bidError || thumbnailError || !hasClaims
                  }
                  label={creatingCollection || updatingCollection ? __('Submitting') : __('Submit')}
                  onClick={handleSubmit}
                />
                <Button button="link" label={__('Cancel')} onClick={() => onDone(collectionId)} />
              </div>
              {submitError ? (
                <ErrorText>{submitError}</ErrorText>
              ) : (
                <p className="help">
                  {__('After submitting, it will take a few minutes for your changes to be live for everyone.')}
                </p>
              )}
              {!isNewCollection && (
                <div className="section__actions">
                  <ClaimAbandonButton uri={uri} abandonActionCallback={() => replace(`/$/${PAGES.LIBRARY}`)} />
                </div>
              )}
            </>
          }
        />
      </div>
    </>
  );
}

export default CollectionForm;