/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Divider, Form, notification, Switch } from 'antd';
import type { FormInstance } from 'antd/es/form';
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { useIntl, useLocation } from 'umi';

import PanelSection from '@/components/PanelSection';

import ActiveCheck from './components/active-check';
import KeepalivePool from './components/KeepalivePool';
import PassHost from './components/PassHost';
import PassiveCheck from './components/passive-check';
import Retries from './components/Retries';
import RetryTimeout from './components/RetryTimeout';
import Scheme from './components/Scheme';
import Timeout from './components/Timeout';
import TLSComponent from './components/TLS';
import Type from './components/Type';
import UpstreamSelector from './components/UpstreamSelector';
import UpstreamType from './components/UpstreamType';
import { convertToRequestData } from './service';

type Upstream = {
  name?: string;
  id?: string;
};

type Props = {
  form: FormInstance;
  disabled?: boolean;
  list?: Upstream[];
  showSelector?: boolean;
  // FIXME: use proper typing
  ref?: any;
  required?: boolean;
  neverReadonly?: boolean;
};

/**
 * UpstreamForm is used to reuse Upstream Form UI,
 * before using this component, we need to execute the following command:
 * form.setFieldsValue(convertToFormData(VALUE_FROM_API))
 */
const UpstreamForm: React.FC<Props> = forwardRef(
  (
    {
      form,
      disabled = false,
      list = [],
      showSelector = false,
      required = true,
      neverReadonly = false,
    },
    ref,
  ) => {
    const location = useLocation();
    const { formatMessage } = useIntl();
    const [readonly, setReadonly] = useState(false);
    const [hiddenForm, setHiddenForm] = useState(false);

    const timeoutFields = [
      {
        label: formatMessage({ id: 'page.upstream.step.connect.timeout' }),
        name: ['timeout', 'connect'],
        desc: formatMessage({ id: 'page.upstream.step.connect.timeout.desc' }),
      },
      {
        label: formatMessage({ id: 'page.upstream.step.send.timeout' }),
        name: ['timeout', 'send'],
        desc: formatMessage({ id: 'page.upstream.step.send.timeout.desc' }),
      },
      {
        label: formatMessage({ id: 'page.upstream.step.read.timeout' }),
        name: ['timeout', 'read'],
        desc: formatMessage({ id: 'page.upstream.step.read.timeout.desc' }),
      },
    ];

    useImperativeHandle(ref, () => ({
      getData: () => convertToRequestData(form.getFieldsValue()),
    }));

    const resetForm = (upstream_id: string) => {
      if (upstream_id === undefined) {
        setReadonly(disabled);
        return;
      }

      if (!neverReadonly) {
        setReadonly(!['Custom', 'None'].includes(upstream_id) || disabled);
      }

      /**
       * upstream_id === None <==> required === false
       * No need to bind Upstream object.
       * When creating Route and binds with a Service, no need to configure Upstream in Route.
       */
      if (upstream_id === 'None') {
        setHiddenForm(true);
        form.resetFields();
        form.setFieldsValue({ upstream_id: 'None' });
        return;
      }

      setHiddenForm(false);

      // NOTE: Use Ant Design's form object to set data automatically
      if (upstream_id === 'Custom') {
        return;
      }

      // NOTE: Set data from Upstream List (Upstream Selector)
      if (list.length === 0) {
        return;
      }
      form.resetFields();
      const targetData = list.find(
        (item) => item.id === upstream_id,
      ) as UpstreamComponent.ResponseData;
      if (targetData) {
        form.setFieldsValue(targetData);
      }
    };

    /**
     * upstream_id
     * - None: No need to bind Upstream to a resource (e.g Service).
     * - Custom: Users could input values on UpstreamForm
     * - Upstream ID from API
     */
    useEffect(() => {
      const upstream_id = form.getFieldValue('upstream_id');
      resetForm(upstream_id);
    }, [form.getFieldValue('upstream_id'), list]);

    const ActiveHealthCheck = () => (
      <React.Fragment>
        <ActiveCheck.Type readonly={readonly} />
        <Form.Item
          noStyle
          shouldUpdate={(prev, next) => prev.checks.active.type !== next.checks.active.type}
        >
          {() => {
            const type = form.getFieldValue(['checks', 'active', 'type']);
            if (['https'].includes(type)) {
              return <ActiveCheck.HttpsVerifyCertificate readonly={readonly} />;
            }
            return null;
          }}
        </Form.Item>
        <ActiveCheck.Timeout readonly={readonly} />
        <ActiveCheck.Concurrency readonly={readonly} />
        <ActiveCheck.Host readonly={readonly} />
        <ActiveCheck.Port readonly={readonly} />
        <ActiveCheck.HttpPath readonly={readonly} />
        <ActiveCheck.ReqHeaders readonly={readonly} />

        <Divider orientation="left" plain>
          {formatMessage({ id: 'page.upstream.step.healthyCheck.healthy.status' })}
        </Divider>

        <ActiveCheck.Healthy.Interval readonly={readonly} />
        <ActiveCheck.Healthy.Successes readonly={readonly} />
        <ActiveCheck.Healthy.HttpStatuses readonly={readonly} />

        <Divider orientation="left" plain>
          {formatMessage({ id: 'page.upstream.step.healthyCheck.unhealthyStatus' })}
        </Divider>

        <ActiveCheck.Unhealthy.Timeouts readonly={readonly} />
        <ActiveCheck.Unhealthy.Interval readonly={readonly} />
        <ActiveCheck.Unhealthy.HttpStatuses readonly={readonly} />
        <ActiveCheck.Unhealthy.HttpFailures readonly={readonly} />
        <ActiveCheck.Unhealthy.TCPFailures readonly={readonly} />
      </React.Fragment>
    );

    const PassiveHealthCheck = () => (
      <React.Fragment>
        <PassiveCheck.Type readonly={readonly} />

        <Divider orientation="left" plain>
          {formatMessage({ id: 'page.upstream.step.healthyCheck.healthy.status' })}
        </Divider>

        <PassiveCheck.Healthy.HttpStatuses readonly={readonly} />
        <PassiveCheck.Healthy.Successes readonly={readonly} />

        <Divider orientation="left" plain>
          {formatMessage({ id: 'page.upstream.step.healthyCheck.unhealthyStatus' })}
        </Divider>

        <PassiveCheck.Unhealthy.Timeouts readonly={readonly} />
        <PassiveCheck.Unhealthy.TcpFailures readonly={readonly} />
        <PassiveCheck.Unhealthy.HttpFailures readonly={readonly} />
        <PassiveCheck.Unhealthy.HttpStatuses readonly={readonly} />
      </React.Fragment>
    );

    const HealthCheckComponent = () => {
      return (
        <PanelSection
          title={formatMessage({ id: 'page.upstream.step.healthyCheck.healthy.check' })}
        >
          <Form.Item
            label={formatMessage({ id: 'page.upstream.step.healthyCheck.active' })}
            name={['custom', 'checks', 'active']}
            valuePropName="checked"
          >
            <Switch disabled={readonly} />
          </Form.Item>
          <Form.Item shouldUpdate noStyle>
            {() => {
              const active = form.getFieldValue(['custom', 'checks', 'active']);
              if (active) {
                return <ActiveHealthCheck />;
              }
              return null;
            }}
          </Form.Item>
          <Divider orientation="left" plain />
          <Form.Item
            label={formatMessage({ id: 'page.upstream.step.healthyCheck.passive' })}
            name={['custom', 'checks', 'passive']}
            valuePropName="checked"
            tooltip={formatMessage({ id: 'component.upstream.other.health-check.passive-only' })}
          >
            <Switch disabled={readonly} />
          </Form.Item>
          <Form.Item
            shouldUpdate={(prev, next) =>
              prev.custom?.checks?.passive !== next.custom?.checks?.passive
            }
            noStyle
          >
            {() => {
              const passive = form.getFieldValue(['custom', 'checks', 'passive']);
              const active = form.getFieldValue(['custom', 'checks', 'active']);
              if (passive) {
                /*
                 * When enable passive check, we should enable active check, too.
                 * When we use form.setFieldsValue to enable active check, error throws.
                 * We choose to alert users first, and need users to enable active check manually.
                 */
                if (!active) {
                  notification.warn({
                    message: formatMessage({ id: 'component.upstream.other.health-check.invalid' }),
                    description: formatMessage({
                      id: 'component.upstream.other.health-check.passive-only',
                    }),
                  });
                }
                return <PassiveHealthCheck />;
              }
              return null;
            }}
          </Form.Item>
        </PanelSection>
      );
    };

    const KeepalivePoolComponent = () => {
      return (
        <PanelSection title={formatMessage({ id: 'page.upstream.step.keepalive_pool' })}>
          <KeepalivePool readonly={readonly} />
        </PanelSection>
      );
    };

    return (
      <Form
        initialValues={{
          upstream_id: !required && location.pathname === '/routes/create' ? 'None' : 'Custom',
        }}
        form={form}
        labelCol={{ span: 3 }}
      >
        {showSelector && (
          <UpstreamSelector
            list={list}
            disabled={disabled}
            required={required}
            onChange={(nextUpstreamId) => {
              resetForm(nextUpstreamId);
            }}
          />
        )}

        {!hiddenForm && (
          <React.Fragment>
            <Type form={form} readonly={readonly} />

            <UpstreamType form={form} readonly={readonly} />

            <PassHost form={form} readonly={readonly} />
            <Retries readonly={readonly} />
            <RetryTimeout readonly={readonly} />

            <Scheme readonly={readonly} />
            <Form.Item noStyle shouldUpdate={(prev, next) => prev.scheme !== next.scheme}>
              {() => {
                const scheme = form.getFieldValue('scheme') as string;
                if (['https', 'grpcs'].includes(scheme)) {
                  return <TLSComponent form={form} readonly={readonly} />;
                }
                return null;
              }}
            </Form.Item>

            {timeoutFields.map((item, index) => (
              <Timeout key={index} {...item} readonly={readonly} />
            ))}

            <KeepalivePoolComponent />

            <HealthCheckComponent />
          </React.Fragment>
        )}
      </Form>
    );
  },
);

export default UpstreamForm;
