import { h, Component } from '/js/web_modules/preact.js';
import htm from '/js/web_modules/htm.js';

const html = htm.bind(h);

export default class FediverseAuth extends Component {
  constructor(props) {
    super(props);

    this.submitButtonPressed = this.submitButtonPressed.bind(this);

    this.state = {
      account: '',
      code: '',
      errorMessage: null,
      loading: false,
      verifying: false,
      valid: false,
    };
  }

  async makeRequest(url, data) {
    const rawResponse = await fetch(url, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    const content = await rawResponse.json();
    if (content.message) {
      this.setState({ errorMessage: content.message, loading: false });
      return;
    }
  }

  switchToCodeVerify() {
    this.setState({ verifying: true, loading: false });
  }

  async validateCodeButtonPressed() {
    const { accessToken } = this.props;
    const { code } = this.state;

    this.setState({ loading: true, errorMessage: null });

    const url = `/api/auth/fediverse/verify?accessToken=${accessToken}`;
    const data = { code: code };

    try {
      await this.makeRequest(url, data);

      // Success. Reload the page.
      window.location = '/';
    } catch (e) {
      console.error(e);
      this.setState({ errorMessage: e, loading: false });
    }
  }

  async registerAccountButtonPressed() {
    const { accessToken } = this.props;
    const { account, valid } = this.state;

    if (!valid) {
      return;
    }

    const url = `/api/auth/fediverse?accessToken=${accessToken}`;
    const normalizedAccount = account.replace(/^@+/, '');
    const data = { account: normalizedAccount };

    this.setState({ loading: true, errorMessage: null });

    try {
      await this.makeRequest(url, data);
      this.switchToCodeVerify();
    } catch (e) {
      console.error(e);
      this.setState({ errorMessage: e, loading: false });
    }
  }

  async submitButtonPressed() {
    const { verifying } = this.state;
    if (verifying) {
      this.validateCodeButtonPressed();
    } else {
      this.registerAccountButtonPressed();
    }
  }

  onInput = (e) => {
    const { value } = e.target;
    const { verifying } = this.state;

    if (verifying) {
      this.setState({ code: value });
      return;
    }

    const valid = validateAccount(value);
    this.setState({ account: value, valid });
  };

  render() {
    const { errorMessage, account, code, valid, loading, verifying } =
      this.state;
    const { authenticated, username } = this.props;
    const buttonState = valid ? '' : 'cursor-not-allowed opacity-50';

    const loaderStyle = loading ? 'flex' : 'none';
    const message = verifying
      ? 'Paste in the code that was sent to your Fediverse account. If you did not receive a code, make sure you can accept direct messages.'
      : !authenticated
      ? html`Receive a direct message on the Fediverse to ${' '} link your
          account to ${' '} <span class="font-bold">${username}</span>, or login
          as a previously linked chat user.`
      : html`<span
          ><b>You are already authenticated</b>. However, you can add other
          accounts or log in as a different user.</span
        >`;
    const label = verifying ? 'Code' : 'Your fediverse account';
    const placeholder = verifying ? '123456' : 'youraccount@fediverse.server';
    const buttonText = verifying ? 'Verify' : 'Authenticate with Fediverse';

    const error = errorMessage
      ? html` <div
          class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
          role="alert"
        >
          <div class="font-bold mb-2">There was an error.</div>
          <div class="block mt-2">
            Server error:
            <div>${errorMessage}</div>
          </div>
        </div>`
      : null;

    return html`
      <div class="bg-gray-100 bg-center bg-no-repeat">
        <p class="text-gray-700 text-md">${message}</p>

        ${error}

        <div class="mb34">
          <label
            class="block text-gray-700 text-sm font-semibold mt-6"
            for="username"
          >
            ${label}
          </label>
          <input
            onInput=${this.onInput}
            type="url"
            value=${verifying ? code : account}
            class="border bg-white rounded w-full py-2 px-3 mb-2 mt-2 text-indigo-700 leading-tight focus:outline-none focus:shadow-outline"
            id="username"
            type="text"
            placeholder=${placeholder}
          />
          <button
            class="bg-indigo-500 hover:bg-indigo-600 text-white font-bold py-2 mt-6 px-4 rounded focus:outline-none focus:shadow-outline ${buttonState}"
            type="button"
            onClick=${this.submitButtonPressed}
          >
            ${buttonText}
          </button>
        </div>

        <p class="mt-4">
          <details>
            <summary class="cursor-pointer">
              Learn more about using the Fediverse to authenticate with chat.
            </summary>
            <div class="inline">
              <p class="mt-4">
                You can link your chat identity with your Fediverse identity.
                Next time you want to use this chat identity you can again go
                through the Fediverse authentication.
              </p>
            </div>
          </details>
        </p>

        <div
          id="follow-loading-spinner-container"
          style="display: ${loaderStyle}"
        >
          <img id="follow-loading-spinner" src="/img/loading.gif" />
          <p class="text-gray-700 text-lg">Authenticating.</p>
          <p class="text-gray-600 text-lg">Please wait...</p>
        </div>
      </div>
    `;
  }
}

function validateAccount(account) {
  account = account.replace(/^@+/, '');
  var regex =
    /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return regex.test(String(account).toLowerCase());
}