/* eslint-disable react/no-unescaped-entities */
import { Typography, Modal, Button, Row, Col, Alert } from 'antd';
import React, { ReactElement, useContext, useEffect, useState, FC } from 'react';
import {
  TEXTFIELD_TYPE_TEXT,
  TEXTFIELD_TYPE_TEXTAREA,
  TEXTFIELD_TYPE_URL,
} from '../../components/admin/TextField';
import { TextFieldWithSubmit } from '../../components/admin/TextFieldWithSubmit';
import { ToggleSwitch } from '../../components/admin/ToggleSwitch';
import { EditValueArray } from '../../components/admin/EditValueArray';
import { UpdateArgs } from '../../types/config-section';
import {
  FIELD_PROPS_ENABLE_FEDERATION,
  TEXTFIELD_PROPS_FEDERATION_LIVE_MESSAGE,
  TEXTFIELD_PROPS_FEDERATION_DEFAULT_USER,
  FIELD_PROPS_FEDERATION_IS_PRIVATE,
  FIELD_PROPS_SHOW_FEDERATION_ENGAGEMENT,
  TEXTFIELD_PROPS_FEDERATION_INSTANCE_URL,
  FIELD_PROPS_FEDERATION_BLOCKED_DOMAINS,
  postConfigUpdateToAPI,
  RESET_TIMEOUT,
  API_FEDERATION_BLOCKED_DOMAINS,
  FIELD_PROPS_FEDERATION_NSFW,
} from '../../utils/config-constants';
import { ServerStatusContext } from '../../utils/server-status-context';
import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses';

import { AdminLayout } from '../../components/layouts/AdminLayout';

export type FederationInfoModalProps = {
  cancelPressed: () => void;
  okPressed: () => void;
};

const FederationInfoModal: FC<FederationInfoModalProps> = ({ cancelPressed, okPressed }) => (
  <Modal
    width="70%"
    title="Enable Social Features"
    visible
    onCancel={cancelPressed}
    footer={
      <div>
        <Button onClick={cancelPressed}>Do not enable</Button>
        <Button type="primary" onClick={okPressed}>
          Enable Social Features
        </Button>
      </div>
    }
  >
    <Typography.Title level={3}>How do Owncast's social features work?</Typography.Title>
    <Typography.Paragraph>
      Owncast's social features are accomplished by having your server join The{' '}
      <a href="https://en.wikipedia.org/wiki/Fediverse" rel="noopener noreferrer" target="_blank">
        Fediverse
      </a>
      , a decentralized, open, collection of independent servers, like yours.
    </Typography.Paragraph>
    Please{' '}
    <a href="https://owncast.online/docs/social" rel="noopener noreferrer" target="_blank">
      read more
    </a>{' '}
    about these features, the details behind them, and how they work.
    <Typography.Paragraph />
    <Typography.Title level={3}>What do you need to know?</Typography.Title>
    <ul>
      <li>
        These features are brand new. Given the variability of interfacing with the rest of the
        world, bugs are possible. Please report anything that you think isn't working quite right.
      </li>
      <li>You must always host your Owncast server with SSL using a https url.</li>
      <li>
        You should not change your server name URL or social username once people begin following
        you, as your server will be seen as a completely different user on the Fediverse, and the
        old user will disappear.
      </li>
      <li>
        Turning on <i>Private mode</i> will allow you to manually approve each follower and limit
        the visibility of your posts to followers only.
      </li>
    </ul>
    <Typography.Title level={3}>Learn more about The Fediverse</Typography.Title>
    <Typography.Paragraph>
      If these concepts are new you should discover more about what this functionality has to offer.
      Visit{' '}
      <a href="https://owncast.online/docs/social" rel="noopener noreferrer" target="_blank">
        our documentation
      </a>{' '}
      to be pointed at some resources that will help get you started on The Fediverse.
    </Typography.Paragraph>
  </Modal>
);

const ConfigFederation = () => {
  const { Title } = Typography;
  const [formDataValues, setFormDataValues] = useState(null);
  const [isInfoModalOpen, setIsInfoModalOpen] = useState(false);
  const serverStatusData = useContext(ServerStatusContext);
  const { serverConfig, setFieldInConfigState } = serverStatusData || {};
  const [blockedDomainSaveState, setBlockedDomainSaveState] = useState(null);

  const { federation, yp, instanceDetails } = serverConfig;
  const { enabled, isPrivate, username, goLiveMessage, showEngagement, blockedDomains } =
    federation;
  const { instanceUrl } = yp;
  const { nsfw } = instanceDetails;

  const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
    setFormDataValues({
      ...formDataValues,
      [fieldName]: value,
    });
  };

  const handleEnabledSwitchChange = (value: boolean) => {
    if (!value) {
      setFormDataValues({
        ...formDataValues,
        enabled: false,
      });
    } else {
      setIsInfoModalOpen(true);
    }
  };

  // if instanceUrl is empty, we should also turn OFF the `enabled` field of directory.
  const handleSubmitInstanceUrl = () => {
    const hasInstanceUrl = formDataValues.instanceUrl !== '';
    const isInstanceUrlSecure = formDataValues.instanceUrl.startsWith('https://');

    if (!hasInstanceUrl || !isInstanceUrlSecure) {
      postConfigUpdateToAPI({
        apiPath: FIELD_PROPS_ENABLE_FEDERATION.apiPath,
        data: { value: false },
      });
      setFormDataValues({
        ...formDataValues,
        enabled: false,
      });
    }
  };

  function federationInfoModalCancelPressed() {
    setIsInfoModalOpen(false);
    setFormDataValues({
      ...formDataValues,
      enabled: false,
    });
  }

  function federationInfoModalOkPressed() {
    setIsInfoModalOpen(false);
    setFormDataValues({
      ...formDataValues,
      enabled: true,
    });
  }

  function resetBlockedDomainsSaveState() {
    setBlockedDomainSaveState(null);
  }

  function saveBlockedDomains() {
    try {
      postConfigUpdateToAPI({
        apiPath: API_FEDERATION_BLOCKED_DOMAINS,
        data: { value: formDataValues.blockedDomains },
        onSuccess: () => {
          setFieldInConfigState({
            fieldName: 'forbiddenUsernames',
            value: formDataValues.forbiddenUsernames,
          });
          setBlockedDomainSaveState(STATUS_SUCCESS);
          setTimeout(resetBlockedDomainsSaveState, RESET_TIMEOUT);
        },
        onError: (message: string) => {
          setBlockedDomainSaveState(createInputStatus(STATUS_ERROR, message));
          setTimeout(resetBlockedDomainsSaveState, RESET_TIMEOUT);
        },
      });
    } catch (e) {
      console.error(e);
      setBlockedDomainSaveState(STATUS_ERROR);
    }
  }

  function handleDeleteBlockedDomain(index: number) {
    formDataValues.blockedDomains.splice(index, 1);
    saveBlockedDomains();
  }

  function handleCreateBlockedDomain(domain: string) {
    let newDomain;
    try {
      const u = new URL(domain);
      newDomain = u.host;
    } catch (_) {
      newDomain = domain;
    }

    formDataValues.blockedDomains.push(newDomain);
    handleFieldChange({
      fieldName: 'blockedDomains',
      value: formDataValues.blockedDomains,
    });
    saveBlockedDomains();
  }

  useEffect(() => {
    setFormDataValues({
      enabled,
      isPrivate,
      username,
      goLiveMessage,
      showEngagement,
      blockedDomains,
      nsfw,
      instanceUrl: yp.instanceUrl,
    });
  }, [serverConfig, yp]);

  if (!formDataValues) {
    return null;
  }

  const hasInstanceUrl = instanceUrl !== '';
  const isInstanceUrlSecure = instanceUrl.startsWith('https://');
  const configurationWarning = !isInstanceUrlSecure && (
    <>
      <Alert
        message="You must set your server URL before you can enable this feature."
        type="warning"
        showIcon
      />
      <br />
      <TextFieldWithSubmit
        fieldName="instanceUrl"
        {...TEXTFIELD_PROPS_FEDERATION_INSTANCE_URL}
        value={formDataValues.instanceUrl}
        initialValue={yp.instanceUrl}
        type={TEXTFIELD_TYPE_URL}
        onChange={handleFieldChange}
        onSubmit={handleSubmitInstanceUrl}
        required
      />
    </>
  );

  const invalidPortWarning = (
    <Alert
      message="Only Owncast instances available on the default SSL port 443 support this feature."
      type="warning"
      showIcon
    />
  );

  const hasInvalidPort =
    instanceUrl && new URL(instanceUrl).port !== '' && new URL(instanceUrl).port !== '443';

  return (
    <div>
      <Title>Configure Social Features</Title>
      <p>
        Owncast provides the ability for people to follow and engage with your instance. It's a
        great way to promote alerting, sharing and engagement of your stream.
      </p>
      <p>
        Once enabled you'll alert your followers when you go live as well as gain the ability to
        compose custom posts to share any information you like.
      </p>
      <p>
        <a href="https://owncast.online/docs/social" rel="noopener noreferrer" target="_blank">
          Read more about the specifics of these social features.
        </a>
      </p>
      <Row>
        <Col span={15} className="form-module" style={{ marginRight: '15px' }}>
          {configurationWarning}
          {hasInvalidPort && invalidPortWarning}
          <ToggleSwitch
            fieldName="enabled"
            onChange={handleEnabledSwitchChange}
            {...FIELD_PROPS_ENABLE_FEDERATION}
            checked={formDataValues.enabled}
            disabled={hasInvalidPort || !hasInstanceUrl || !isInstanceUrlSecure}
          />
          <ToggleSwitch
            fieldName="isPrivate"
            {...FIELD_PROPS_FEDERATION_IS_PRIVATE}
            checked={formDataValues.isPrivate}
            disabled={!enabled}
          />
          <ToggleSwitch
            fieldName="nsfw"
            useSubmit
            {...FIELD_PROPS_FEDERATION_NSFW}
            checked={formDataValues.nsfw}
            disabled={hasInvalidPort || !hasInstanceUrl}
          />
          <TextFieldWithSubmit
            required
            fieldName="username"
            type={TEXTFIELD_TYPE_TEXT}
            {...TEXTFIELD_PROPS_FEDERATION_DEFAULT_USER}
            value={formDataValues.username}
            initialValue={username}
            onChange={handleFieldChange}
            disabled={!enabled}
          />
          <TextFieldWithSubmit
            fieldName="goLiveMessage"
            {...TEXTFIELD_PROPS_FEDERATION_LIVE_MESSAGE}
            type={TEXTFIELD_TYPE_TEXTAREA}
            value={formDataValues.goLiveMessage}
            initialValue={goLiveMessage}
            onChange={handleFieldChange}
            disabled={!enabled}
          />
          <ToggleSwitch
            fieldName="showEngagement"
            {...FIELD_PROPS_SHOW_FEDERATION_ENGAGEMENT}
            checked={formDataValues.showEngagement}
            disabled={!enabled}
          />
        </Col>
        <Col span={8} className="form-module">
          <EditValueArray
            title={FIELD_PROPS_FEDERATION_BLOCKED_DOMAINS.label}
            placeholder={FIELD_PROPS_FEDERATION_BLOCKED_DOMAINS.placeholder}
            description={FIELD_PROPS_FEDERATION_BLOCKED_DOMAINS.tip}
            values={formDataValues.blockedDomains}
            handleDeleteIndex={handleDeleteBlockedDomain}
            handleCreateString={handleCreateBlockedDomain}
            submitStatus={createInputStatus(blockedDomainSaveState)}
          />
        </Col>
      </Row>
      {isInfoModalOpen && (
        <FederationInfoModal
          cancelPressed={federationInfoModalCancelPressed}
          okPressed={federationInfoModalOkPressed}
        />
      )}
    </div>
  );
};

ConfigFederation.getLayout = function getLayout(page: ReactElement) {
  return <AdminLayout page={page} />;
};

export default ConfigFederation;
