/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import React from "react";
import { TextInput } from "./TextInput";

export function IPInput(p: {
  label: string;
  editable: boolean;
  value?: string;
  onValueChange?: (newVal: string | undefined) => void;
  version: 4 | 6;
}): React.ReactElement {
  const { onValueChange, ...props } = p;
  return (
    <TextInput
      onValueChange={(v) => {
        onValueChange?.(p.version === 4 ? sanitizeIpV4(v) : sanitizeIpV6(v));
      }}
      {...props}
    />
  );
}

export function IPInputWithMask(p: {
  label: string;
  editable: boolean;
  ipAndMask?: string;
  ip?: string;
  mask?: number;
  onValueChange?: (ip?: string, mask?: number, ipAndMask?: string) => void;
  version: 4 | 6;
}): React.ReactElement {
  const showSlash = React.useRef(!!p.mask);

  const currValue =
    p.ipAndMask ??
    `${p.ip ?? ""}${p.mask || showSlash.current ? "/" : ""}${p.mask ?? ""}`;

  const { onValueChange, ...props } = p;
  return (
    <TextInput
      onValueChange={(v) => {
        showSlash.current = false;
        if (!v) {
          onValueChange?.(undefined, undefined, undefined);
          return;
        }

        const split = v.split("/");
        const ip =
          p.version === 4 ? sanitizeIpV4(split[0]) : sanitizeIpV6(split[0]);
        let mask = undefined;

        if (split.length > 1) {
          showSlash.current = true;
          mask = sanitizeMask(p.version, split[1]);
        }

        onValueChange?.(
          ip,
          mask,
          mask || showSlash.current ? `${ip}/${mask ?? ""}` : ip
        );
      }}
      value={currValue}
      {...props}
    />
  );
}

function sanitizeIpV4(s: string | undefined): string | undefined {
  if (s === "" || s === undefined) return s;

  const split = s.split(".");
  if (split.length > 4) split.splice(4);

  let needAnotherIteration = false;

  const res = split
    .map((v) => {
      if (v === "") return "";

      const num = Number(v);
      if (isNaN(num) || num < 0) return "0";
      if (num > 255) {
        needAnotherIteration = true;
        return v.slice(0, 2) + "." + v.slice(2);
      }
      return num.toString();
    })
    .join(".");

  return needAnotherIteration ? sanitizeIpV4(res) : res;
}

function sanitizeIpV6(s: string | undefined): string | undefined {
  if (s === "" || s === undefined) return s;

  const split = s.split(":");
  if (split.length > 8) split.splice(8);

  let needAnotherIteration = false;

  let res = split
    .map((e) => {
      if (e === "") return e;

      const num = parseInt(e, 16);
      if (isNaN(num)) return "0";

      const s = num.toString(16);
      if (num > 0xffff) {
        needAnotherIteration = true;
        return s.slice(0, 4) + ":" + s.slice(4);
      }

      return s;
    })
    .join(":");

  const firstIndex = res.indexOf("::");
  let nextIndex = res.lastIndexOf("::");
  while (nextIndex !== firstIndex) {
    res = res.slice(0, nextIndex) + res.slice(nextIndex + 1);
    nextIndex = res.lastIndexOf("::");
  }

  return needAnotherIteration ? sanitizeIpV6(res) : res;
}

function sanitizeMask(version: 4 | 6, mask?: string): number | undefined {
  if (!mask) return undefined;

  const value = Math.floor(Number(mask));

  if (version === 4) {
    return value < 0 || value > 32 ? 32 : value;
  } else {
    return value < 0 || value > 128 ? 128 : value;
  }
}
