/*
 * 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 React, { useState, forwardRef, useImperativeHandle, useEffect } from 'react';
import { useIntl } from 'umi';
import type { FormInstance } from 'antd/es/form';

import PanelSection from '@/components/PanelSection';
import PassiveCheck from './components/passive-check';
import ActiveCheck from './components/active-check'
import Nodes from './components/Nodes'
import Scheme from './components/Scheme';
import Timeout from './components/Timeout';
import Type from './components/Type';
import UpstreamSelector from './components/UpstreamSelector';
import Retries from './components/Retries';
import PassHost from './components/PassHost';
import TLSComponent from './components/TLS';
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 { 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>
      )
    }

    return (
      <Form
        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} />
            <Nodes readonly={readonly} />

            <PassHost form={form} readonly={readonly} />
            <Retries 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} />
            ))}

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

export default UpstreamForm;
