import { Component, h, Fragment, createRef } from 'preact';
import cls from 'classnames';
import { AnnotationSource } from '../storage/AnnotationSource';
import classes from './AnnotationStoreSelector.module.scss';

interface AnnotationStoreSelectorProps {}

interface AnnotationStoreSelectorState {
  possiblyWritableSources?: AnnotationSource[];
  selectedSource?: AnnotationSource | null;
  testSuccess?: boolean;
}

export class AnnotationStoreSelector extends Component<
  AnnotationStoreSelectorProps,
  AnnotationStoreSelectorState
> {
  state: AnnotationStoreSelectorState = {};

  selectElement = createRef<HTMLSelectElement>();
  testButton = createRef<HTMLButtonElement>();

  override async componentDidMount() {
    await this.loadData();
  }

  async loadData() {
    const possiblyWritableSources =
      await AnnotationSource.getPossiblyWritableSources();
    const selectedSource = possiblyWritableSources.find(
      (source) => source.data.useForNewAnnotations,
    );
    this.setState({
      possiblyWritableSources: possiblyWritableSources,
      selectedSource,
    });
  }

  async onChange() {
    const optionValue = this.selectElement.current?.value;
    const selectedSourceId = optionValue ? +optionValue : null;
    const selectedSource = selectedSourceId
      ? await AnnotationSource.get(selectedSourceId)
      : null;
    this.setState({ selectedSource, testSuccess: false });
  }

  async testCreateAnnotation() {
    this.testButton.current?.classList.add(classes.loading);
    const { selectedSource } = this.state;
    try {
      if (!selectedSource) throw new Error('No source selected to test.');
      await selectedSource.testWritable();
      this.setState({ testSuccess: true });

      // Flag the selected source, unflag the others.
      const allSources = await AnnotationSource.getAll();
      await Promise.all(
        allSources.map((source) =>
          source.useForNewAnnotations(
            source.data._id === selectedSource.data._id,
          ),
        ),
      );
      await this.loadData();
    } catch (error) {
      this.setState({ testSuccess: false });
      alert(error);
    } finally {
      this.testButton.current?.classList.remove(classes.loading);
    }
  }

  render(
    {}: AnnotationStoreSelectorProps,
    {
      possiblyWritableSources,
      selectedSource,
      testSuccess,
    }: AnnotationStoreSelectorState,
  ) {
    // List sources, sorting previously connected ones first.
    const optionsList = possiblyWritableSources
      ?.sort((a, b) =>
        a.data.writable === b.data.writable ? 0 : b.data.writable ? 1 : -1,
      )
      .map((source) => (
        <option
          value={source.data._id}
          selected={source.data._id === selectedSource?.data._id}
        >
          {source.data.writable ? '✓' : '？'} {source.data.title} &lt;
          {source.data.url}&gt;
        </option>
      ));

    if (optionsList?.length === 0) {
      return (
        <p>
          To create annotations, first subscribe to a collection (that you have
          write access to).
        </p>
      );
    }

    return (
      <>
        <p>Choose the collection to store/publish annotations you create:</p>
        <select
          ref={this.selectElement}
          value={`${selectedSource?.data._id ?? ''}`}
          onChange={(e) => this.onChange()}
          class={classes.select}
        >
          <option value="">None</option>
          {optionsList}
        </select>
        {selectedSource && (
          <>
            <button
              onClick={() => this.testCreateAnnotation()}
              title="Click to test if you can create an annotation in the chosen collection. Your browser may prompt you to provide login credentials."
              ref={this.testButton}
              class={cls(classes.button, { [classes.success]: testSuccess })}
            >
              {testSuccess
                ? 'Connected'
                : selectedSource.data.writable
                ? selectedSource.data.needsAuth
                  ? 'Reconnect'
                  : 'Check'
                : 'Connect'}
            </button>
            {selectedSource.data.writable && selectedSource.data.needsAuth && (
              <p>
                Your browser appears to have lost write access to the
                collection. Please click reconnect to try log in again.
              </p>
            )}
          </>
        )}
      </>
    );
  }
}
