import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
import { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { FormInstance } from 'antd/es/form';
import type { NamePath } from 'antd/es/form/interface';

import { RandomUtil } from '@/utils';
import { OutboundProtocols } from '@/schemas/primitives';

// Pattern A FinalMaskForm. Renders a Fragment of Form.Items at absolute
// paths under `name`; the parent modal owns the Form instance.
//
// Naming convention inside Form.List: AntD prefixes Form.Item `name`
// with the Form.List's own `name`. So Form.Items inside the render
// prop use RELATIVE paths (e.g. `[field.name, 'type']`). Nested
// Form.Lists also use relative names. Using absolute paths here would
// double up the prefix and silently route reads/writes to the wrong
// storage path.

export interface FinalMaskFormProps {
  name: NamePath;
  network: string;
  protocol: string;
  form: FormInstance;
}

const TCP_NETWORKS = ['raw', 'tcp', 'httpupgrade', 'ws', 'grpc', 'xhttp'];

function asPath(name: NamePath): (string | number)[] {
  return Array.isArray(name) ? [...name] : [name];
}

function defaultTcpMaskSettings(type: string): Record<string, unknown> {
  switch (type) {
    case 'fragment':
      return { packets: '1-3', length: '', delay: '', maxSplit: '' };
    case 'sudoku':
      return {
        password: '', ascii: '', customTable: '', customTables: '',
        paddingMin: 0, paddingMax: 0,
      };
    case 'header-custom':
      return { clients: [], servers: [] };
    default:
      return {};
  }
}

function defaultUdpMaskSettings(type: string): Record<string, unknown> {
  switch (type) {
    case 'salamander':
    case 'mkcp-aes128gcm':
      return { password: '' };
    case 'header-dns':
      return { domain: '' };
    case 'xdns':
      return { domains: [] };
    case 'xicmp':
      return { ip: '0.0.0.0', id: 0 };
    case 'header-custom':
      return { client: [], server: [] };
    case 'noise':
      return { reset: 0, noise: [] };
    default:
      return {};
  }
}

function defaultClientServerItem(): Record<string, unknown> {
  return { delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: [] };
}

function defaultUdpClientServerItem(): Record<string, unknown> {
  return { rand: 0, randRange: '0-255', type: 'array', packet: [] };
}

function defaultNoiseItem(): Record<string, unknown> {
  return {
    rand: '1-8192', randRange: '0-255', type: 'array', packet: [], delay: '10-20',
  };
}

function defaultQuicParams(): Record<string, unknown> {
  return {
    congestion: 'bbr',
    debug: false,
    maxIdleTimeout: 30,
    keepAlivePeriod: 10,
    disablePathMTUDiscovery: false,
    maxIncomingStreams: 1024,
    initStreamReceiveWindow: 8388608,
    maxStreamReceiveWindow: 8388608,
    initConnectionReceiveWindow: 20971520,
    maxConnectionReceiveWindow: 20971520,
  };
}

function defaultUdpHop(): Record<string, unknown> {
  return { ports: '20000-50000', interval: '5-10' };
}

export default function FinalMaskForm({ name, network, protocol, form }: FinalMaskFormProps) {
  const base = asPath(name);
  const isHysteria = protocol === OutboundProtocols.Hysteria || protocol === 'hysteria';
  const showTcp = TCP_NETWORKS.includes(network);
  const showUdp = isHysteria || network === 'kcp';
  const showQuic = isHysteria || network === 'xhttp';
  const quicParams = Form.useWatch([...base, 'quicParams'], { form, preserve: true });
  const hasQuicParams = quicParams != null;

  if (!showTcp && !showUdp && !showQuic) return null;

  return (
    <>
      {showTcp && <TcpMasksList base={base} form={form} />}
      {showUdp && <UdpMasksList base={base} form={form} isHysteria={isHysteria} network={network} />}
      {showQuic && (
        <>
          <Form.Item label="QUIC Params">
            <Switch
              checked={hasQuicParams}
              onChange={(v) => {
                form.setFieldValue([...base, 'quicParams'], v ? defaultQuicParams() : undefined);
              }}
            />
          </Form.Item>
          {hasQuicParams && <QuicParamsForm base={[...base, 'quicParams']} form={form} />}
        </>
      )}
    </>
  );
}

function TcpMasksList({ base, form }: { base: (string | number)[]; form: FormInstance }) {
  return (
    <Form.List name={[...base, 'tcp']}>
      {(fields, { add, remove }) => (
        <>
          <Form.Item label="TCP Masks">
            <Button
              type="primary"
              size="small"
              icon={<PlusOutlined />}
              onClick={() => add({ type: 'fragment', settings: defaultTcpMaskSettings('fragment') })}
            />
          </Form.Item>
          {fields.map((field, mIdx) => (
            <TcpMaskItem
              key={field.key}
              fieldName={field.name}
              displayIndex={mIdx + 1}
              form={form}
              listPath={[...base, 'tcp']}
              onRemove={() => remove(field.name)}
            />
          ))}
        </>
      )}
    </Form.List>
  );
}

function TcpMaskItem({
  fieldName, displayIndex, form, listPath, onRemove,
}: {
  fieldName: number;
  displayIndex: number;
  form: FormInstance;
  listPath: (string | number)[];
  onRemove: () => void;
}) {
  // Absolute path for setFieldValue side effects (resetting settings on
  // type change). All Form.Item `name=` use RELATIVE paths within the
  // outer Form.List context.
  const absolutePath = [...listPath, fieldName];

  return (
    <div>
      <Divider style={{ margin: 0 }}>
        TCP Mask {displayIndex}
        <DeleteOutlined className="danger-icon" onClick={onRemove} />
      </Divider>

      <Form.Item label="Type" name={[fieldName, 'type']}>
        <Select
          onChange={(v) =>
            form.setFieldValue([...absolutePath, 'settings'], defaultTcpMaskSettings(v))
          }
          options={[
            { value: 'fragment', label: 'Fragment' },
            { value: 'header-custom', label: 'Header Custom' },
            { value: 'sudoku', label: 'Sudoku' },
          ]}
        />
      </Form.Item>

      <Form.Item
        noStyle
        shouldUpdate={(prev, curr) => {
          const a = getDeep(prev, [...absolutePath, 'type']);
          const b = getDeep(curr, [...absolutePath, 'type']);
          return a !== b;
        }}
      >
        {({ getFieldValue }) => {
          const type = getFieldValue([...absolutePath, 'type']) as string | undefined;
          if (type === 'fragment') {
            return (
              <>
                <Form.Item label="Packets" name={[fieldName, 'settings', 'packets']}>
                  <Select
                    options={[
                      { value: 'tlshello', label: 'tlshello' },
                      { value: '1-3', label: '1-3' },
                      { value: '1-5', label: '1-5' },
                    ]}
                  />
                </Form.Item>
                <Form.Item label="Length" name={[fieldName, 'settings', 'length']}>
                  <Input />
                </Form.Item>
                <Form.Item label="Delay" name={[fieldName, 'settings', 'delay']}>
                  <Input />
                </Form.Item>
                <Form.Item label="Max Split" name={[fieldName, 'settings', 'maxSplit']}>
                  <Input />
                </Form.Item>
              </>
            );
          }
          if (type === 'sudoku') {
            return (
              <>
                <Form.Item label="Password" name={[fieldName, 'settings', 'password']}><Input /></Form.Item>
                <Form.Item label="ASCII" name={[fieldName, 'settings', 'ascii']}><Input /></Form.Item>
                <Form.Item label="Custom Table" name={[fieldName, 'settings', 'customTable']}><Input /></Form.Item>
                <Form.Item label="Custom Tables" name={[fieldName, 'settings', 'customTables']}><Input /></Form.Item>
                <Form.Item label="Padding Min" name={[fieldName, 'settings', 'paddingMin']}>
                  <InputNumber min={0} />
                </Form.Item>
                <Form.Item label="Padding Max" name={[fieldName, 'settings', 'paddingMax']}>
                  <InputNumber min={0} />
                </Form.Item>
              </>
            );
          }
          if (type === 'header-custom') {
            return (
              <HeaderCustomGroups
                tcpFieldName={fieldName}
                form={form}
                absoluteSettingsPath={[...absolutePath, 'settings']}
              />
            );
          }
          return null;
        }}
      </Form.Item>
    </div>
  );
}

// Walks a deep object path safely. Used inside shouldUpdate which gets
// the whole form values blob; we need to compare a deep field across
// prev/curr without crashing on missing intermediates.
function getDeep(obj: unknown, path: (string | number)[]): unknown {
  let cur: unknown = obj;
  for (const key of path) {
    if (cur == null || typeof cur !== 'object') return undefined;
    cur = (cur as Record<string | number, unknown>)[key];
  }
  return cur;
}

function HeaderCustomGroups({
  tcpFieldName, form, absoluteSettingsPath,
}: {
  tcpFieldName: number;
  form: FormInstance;
  absoluteSettingsPath: (string | number)[];
}) {
  return (
    <>
      {(['clients', 'servers'] as const).map((groupKey) => (
        <Form.List key={groupKey} name={[tcpFieldName, 'settings', groupKey]}>
          {(groups, { add: addGroup, remove: removeGroup }) => (
            <>
              <Form.Item label={groupKey === 'clients' ? 'Clients' : 'Servers'}>
                <Button
                  type="primary"
                  size="small"
                  icon={<PlusOutlined />}
                  onClick={() => addGroup([defaultClientServerItem()])}
                />
              </Form.Item>
              {groups.map((group, gi) => (
                <div key={group.key}>
                  <Divider style={{ margin: 0 }}>
                    {groupKey === 'clients' ? 'Clients' : 'Servers'} Group {gi + 1}
                    <DeleteOutlined className="danger-icon" onClick={() => removeGroup(group.name)} />
                  </Divider>
                  <Form.List name={[group.name]}>
                    {(items, { add: addItem, remove: removeItem }) => (
                      <>
                        <Form.Item label="Items">
                          <Button
                            size="small"
                            icon={<PlusOutlined />}
                            onClick={() => addItem(defaultClientServerItem())}
                          />
                        </Form.Item>
                        {items.map((item) => (
                          <ItemEditor
                            key={item.key}
                            fieldName={item.name}
                            form={form}
                            absoluteItemPath={[...absoluteSettingsPath, groupKey, group.name, item.name]}
                            delayMode="number"
                            onRemove={() => removeItem(item.name)}
                          />
                        ))}
                      </>
                    )}
                  </Form.List>
                </div>
              ))}
            </>
          )}
        </Form.List>
      ))}
    </>
  );
}

function UdpMasksList({
  base, form, isHysteria, network,
}: { base: (string | number)[]; form: FormInstance; isHysteria: boolean; network: string }) {
  return (
    <Form.List name={[...base, 'udp']}>
      {(fields, { add, remove }) => (
        <>
          <Form.Item label="UDP Masks">
            <Button
              type="primary"
              size="small"
              icon={<PlusOutlined />}
              onClick={() => {
                const def = isHysteria ? 'salamander' : 'mkcp-aes128gcm';
                add({ type: def, settings: defaultUdpMaskSettings(def) });
              }}
            />
          </Form.Item>
          {fields.map((field, mIdx) => (
            <UdpMaskItem
              key={field.key}
              fieldName={field.name}
              displayIndex={mIdx + 1}
              form={form}
              listPath={[...base, 'udp']}
              isHysteria={isHysteria}
              network={network}
              onRemove={() => remove(field.name)}
            />
          ))}
        </>
      )}
    </Form.List>
  );
}

function UdpMaskItem({
  fieldName, displayIndex, form, listPath, isHysteria, network, onRemove,
}: {
  fieldName: number;
  displayIndex: number;
  form: FormInstance;
  listPath: (string | number)[];
  isHysteria: boolean;
  network: string;
  onRemove: () => void;
}) {
  const absolutePath = [...listPath, fieldName];

  const onTypeChange = (v: string) => {
    form.setFieldValue([...absolutePath, 'settings'], defaultUdpMaskSettings(v));
    if (network === 'kcp') {
      const kcpMtuPath = [...listPath.slice(0, -1), 'kcpSettings', 'mtu'];
      form.setFieldValue(kcpMtuPath, v === 'xdns' ? 900 : 1350);
    }
  };

  const options = isHysteria
    ? [{ value: 'salamander', label: 'Salamander (Hysteria2)' }]
    : [
        { value: 'mkcp-aes128gcm', label: 'mKCP AES-128-GCM' },
        { value: 'header-dns', label: 'Header DNS' },
        { value: 'header-dtls', label: 'Header DTLS 1.2' },
        { value: 'header-srtp', label: 'Header SRTP' },
        { value: 'header-utp', label: 'Header uTP' },
        { value: 'header-wechat', label: 'Header WeChat Video' },
        { value: 'header-wireguard', label: 'Header WireGuard' },
        { value: 'mkcp-original', label: 'mKCP Original' },
        { value: 'xdns', label: 'xDNS' },
        { value: 'xicmp', label: 'xICMP' },
        { value: 'header-custom', label: 'Header Custom' },
        { value: 'noise', label: 'Noise' },
      ];

  return (
    <div>
      <Divider style={{ margin: 0 }}>
        UDP Mask {displayIndex}
        <DeleteOutlined className="danger-icon" onClick={onRemove} />
      </Divider>

      <Form.Item label="Type" name={[fieldName, 'type']}>
        <Select onChange={onTypeChange} options={options} />
      </Form.Item>

      <Form.Item
        noStyle
        shouldUpdate={(prev, curr) => getDeep(prev, [...absolutePath, 'type']) !== getDeep(curr, [...absolutePath, 'type'])}
      >
        {({ getFieldValue }) => {
          const type = getFieldValue([...absolutePath, 'type']) as string | undefined;
          if (type === 'mkcp-aes128gcm' || type === 'salamander') {
            return (
              <Form.Item label="Password" name={[fieldName, 'settings', 'password']}>
                <Input placeholder="Obfuscation password" />
              </Form.Item>
            );
          }
          if (type === 'header-dns') {
            return (
              <Form.Item label="Domain" name={[fieldName, 'settings', 'domain']}>
                <Input placeholder="e.g., www.example.com" />
              </Form.Item>
            );
          }
          if (type === 'xdns') {
            return (
              <Form.Item label="Domains" name={[fieldName, 'settings', 'domains']}>
                <Select mode="tags" style={{ width: '100%' }} tokenSeparators={[',']} />
              </Form.Item>
            );
          }
          if (type === 'xicmp') {
            return (
              <>
                <Form.Item label="IP" name={[fieldName, 'settings', 'ip']}>
                  <Input placeholder="0.0.0.0" />
                </Form.Item>
                <Form.Item label="ID" name={[fieldName, 'settings', 'id']}>
                  <InputNumber min={0} />
                </Form.Item>
              </>
            );
          }
          if (type === 'header-custom') {
            return (
              <UdpHeaderCustom
                udpFieldName={fieldName}
                form={form}
                absoluteSettingsPath={[...absolutePath, 'settings']}
              />
            );
          }
          if (type === 'noise') {
            return (
              <NoiseItems
                udpFieldName={fieldName}
                form={form}
                absoluteSettingsPath={[...absolutePath, 'settings']}
              />
            );
          }
          return null;
        }}
      </Form.Item>
    </div>
  );
}

function UdpHeaderCustom({
  udpFieldName, form, absoluteSettingsPath,
}: {
  udpFieldName: number;
  form: FormInstance;
  absoluteSettingsPath: (string | number)[];
}) {
  return (
    <>
      {(['client', 'server'] as const).map((groupKey) => (
        <Form.List key={groupKey} name={[udpFieldName, 'settings', groupKey]}>
          {(items, { add, remove }) => (
            <>
              <Form.Item label={groupKey === 'client' ? 'Client' : 'Server'}>
                <Button
                  type="primary"
                  size="small"
                  icon={<PlusOutlined />}
                  onClick={() => add(defaultUdpClientServerItem())}
                />
              </Form.Item>
              {items.map((item, ci) => (
                <div key={item.key}>
                  <Divider style={{ margin: 0 }}>
                    {groupKey === 'client' ? 'Client' : 'Server'} {ci + 1}
                    <DeleteOutlined className="danger-icon" onClick={() => remove(item.name)} />
                  </Divider>
                  <ItemEditor
                    fieldName={item.name}
                    form={form}
                    absoluteItemPath={[...absoluteSettingsPath, groupKey, item.name]}
                    onRemove={() => remove(item.name)}
                  />
                </div>
              ))}
            </>
          )}
        </Form.List>
      ))}
    </>
  );
}

function NoiseItems({
  udpFieldName, form, absoluteSettingsPath,
}: {
  udpFieldName: number;
  form: FormInstance;
  absoluteSettingsPath: (string | number)[];
}) {
  return (
    <>
      <Form.Item label="Reset" name={[udpFieldName, 'settings', 'reset']}>
        <InputNumber min={0} />
      </Form.Item>
      <Form.List name={[udpFieldName, 'settings', 'noise']}>
        {(items, { add, remove }) => (
          <>
            <Form.Item label="Noise">
              <Button
                type="primary"
                size="small"
                icon={<PlusOutlined />}
                onClick={() => add(defaultNoiseItem())}
              />
            </Form.Item>
            {items.map((item, ni) => (
              <div key={item.key}>
                <Divider style={{ margin: 0 }}>
                  Noise {ni + 1}
                  <DeleteOutlined className="danger-icon" onClick={() => remove(item.name)} />
                </Divider>
                <ItemEditor
                  fieldName={item.name}
                  form={form}
                  absoluteItemPath={[...absoluteSettingsPath, 'noise', item.name]}
                  delayMode="string"
                  onRemove={() => remove(item.name)}
                />
              </div>
            ))}
          </>
        )}
      </Form.List>
    </>
  );
}

function ItemEditor({
  fieldName, form, absoluteItemPath, delayMode, onRemove: _onRemove,
}: {
  fieldName: number;
  form: FormInstance;
  absoluteItemPath: (string | number)[];
  delayMode?: 'number' | 'string';
  onRemove?: () => void;
}) {
  const onTypeChange = (v: string) => {
    if (v === 'base64') {
      form.setFieldValue([...absoluteItemPath, 'packet'], RandomUtil.randomBase64());
    } else if (v === 'array') {
      form.setFieldValue([...absoluteItemPath, 'rand'], delayMode === 'string' ? '1-8192' : 0);
      form.setFieldValue([...absoluteItemPath, 'packet'], []);
    } else {
      form.setFieldValue([...absoluteItemPath, 'packet'], '');
    }
  };

  return (
    <>
      <Form.Item label="Type" name={[fieldName, 'type']}>
        <Select
          onChange={onTypeChange}
          options={[
            { value: 'array', label: 'Array' },
            { value: 'str', label: 'String' },
            { value: 'hex', label: 'Hex' },
            { value: 'base64', label: 'Base64' },
          ]}
        />
      </Form.Item>

      {delayMode === 'number' && (
        <Form.Item label="Delay (ms)" name={[fieldName, 'delay']}>
          <InputNumber min={0} />
        </Form.Item>
      )}
      {delayMode === 'string' && (
        <Form.Item label="Delay" name={[fieldName, 'delay']}>
          <Input placeholder="10-20" />
        </Form.Item>
      )}

      <Form.Item
        noStyle
        shouldUpdate={(prev, curr) => getDeep(prev, [...absoluteItemPath, 'type']) !== getDeep(curr, [...absoluteItemPath, 'type'])}
      >
        {({ getFieldValue }) => {
          const type = getFieldValue([...absoluteItemPath, 'type']) as string | undefined;
          if (type === 'array') {
            return (
              <>
                <Form.Item label="Rand" name={[fieldName, 'rand']}>
                  {delayMode === 'string' ? (
                    <Input placeholder="0 or 1-8192" />
                  ) : (
                    <InputNumber min={0} />
                  )}
                </Form.Item>
                <Form.Item label="Rand Range" name={[fieldName, 'randRange']}>
                  <Input placeholder="0-255" />
                </Form.Item>
              </>
            );
          }
          if (type === 'base64') {
            return (
              <Form.Item label="Packet">
                <Space.Compact block>
                  <Form.Item name={[fieldName, 'packet']} noStyle>
                    <Input placeholder="binary data" style={{ width: 'calc(100% - 32px)' }} />
                  </Form.Item>
                  <Button
                    icon={<ReloadOutlined />}
                    onClick={() => form.setFieldValue([...absoluteItemPath, 'packet'], RandomUtil.randomBase64())}
                  />
                </Space.Compact>
              </Form.Item>
            );
          }
          return (
            <Form.Item label="Packet" name={[fieldName, 'packet']}>
              <Input placeholder="binary data" />
            </Form.Item>
          );
        }}
      </Form.Item>
    </>
  );
}

function QuicParamsForm({ base, form }: { base: (string | number)[]; form: FormInstance }) {
  const congestion = Form.useWatch([...base, 'congestion'], form) as string | undefined;
  const udpHop = Form.useWatch([...base, 'udpHop'], { form, preserve: true }) as Record<string, unknown> | undefined;
  const hasUdpHop = udpHop != null;

  return (
    <>
      <Form.Item label="Congestion" name={[...base, 'congestion']}>
        <Select
          options={[
            { value: 'reno', label: 'Reno' },
            { value: 'bbr', label: 'BBR' },
            { value: 'brutal', label: 'Brutal' },
            { value: 'force-brutal', label: 'Force Brutal' },
          ]}
        />
      </Form.Item>
      {congestion === 'bbr' && (
        <Form.Item label="BBR Profile" name={[...base, 'bbrProfile']}>
          <Select
            allowClear
            placeholder="standard"
            options={[
              { value: 'conservative', label: 'Conservative' },
              { value: 'standard', label: 'Standard' },
              { value: 'aggressive', label: 'Aggressive' },
            ]}
          />
        </Form.Item>
      )}
      <Form.Item label="Debug" name={[...base, 'debug']} valuePropName="checked">
        <Switch />
      </Form.Item>

      {(congestion === 'brutal' || congestion === 'force-brutal') && (
        <>
          <Form.Item label="Brutal Up" name={[...base, 'brutalUp']}>
            <Input placeholder="e.g. 60 mbps" />
          </Form.Item>
          <Form.Item label="Brutal Down" name={[...base, 'brutalDown']}>
            <Input placeholder="e.g. 100 mbps" />
          </Form.Item>
        </>
      )}

      <Form.Item label="UDP Hop">
        <Switch
          checked={hasUdpHop}
          onChange={(v) => {
            form.setFieldValue([...base, 'udpHop'], v ? defaultUdpHop() : undefined);
          }}
        />
      </Form.Item>
      {hasUdpHop && (
        <>
          <Form.Item label="Hop Ports" name={[...base, 'udpHop', 'ports']}>
            <Input placeholder="e.g. 20000-50000" />
          </Form.Item>
          <Form.Item label="Hop Interval (s)" name={[...base, 'udpHop', 'interval']}>
            <Input placeholder="e.g. 5-10" />
          </Form.Item>
        </>
      )}

      <Form.Item label="Max Idle Timeout (s)" name={[...base, 'maxIdleTimeout']}>
        <InputNumber min={4} max={120} />
      </Form.Item>
      <Form.Item label="Keep Alive Period (s)" name={[...base, 'keepAlivePeriod']}>
        <InputNumber min={2} max={60} />
      </Form.Item>
      <Form.Item label="Disable Path MTU Dis" name={[...base, 'disablePathMTUDiscovery']} valuePropName="checked">
        <Switch />
      </Form.Item>

      <Form.Item label="Max Incoming Streams" name={[...base, 'maxIncomingStreams']}>
        <InputNumber min={8} placeholder="1024 = default" />
      </Form.Item>
      <Form.Item label="Init Stream Window" name={[...base, 'initStreamReceiveWindow']}>
        <InputNumber min={16384} placeholder="8388608 = default" />
      </Form.Item>
      <Form.Item label="Max Stream Window" name={[...base, 'maxStreamReceiveWindow']}>
        <InputNumber min={16384} placeholder="8388608 = default" />
      </Form.Item>
      <Form.Item label="Init Conn Window" name={[...base, 'initConnectionReceiveWindow']}>
        <InputNumber min={16384} placeholder="20971520 = default" />
      </Form.Item>
      <Form.Item label="Max Conn Window" name={[...base, 'maxConnectionReceiveWindow']}>
        <InputNumber min={16384} placeholder="20971520 = default" />
      </Form.Item>
    </>
  );
}
