export interface QuickConnectTarget {
  hostname: string;
  username?: string;
  port?: number;
}

interface QuickConnectParseResult {
  target: QuickConnectTarget | null;
  warnings: string[];
}

/** Test whether a string looks like a bare (un-bracketed) IPv6 address.
 *  Must have only hex digits and colons, with either:
 *  - A "::" shorthand (unambiguously IPv6), or
 *  - Exactly 7 colons (full 8-group notation like 2607:f130:0:179:0:0:b0df:eec4)
 *  This avoids false positives on MAC addresses (6 groups, 5 colons). */
const BARE_IPV6_RE = /^[a-fA-F0-9:]+$/;
const isBareIPv6 = (s: string): boolean => {
  if (!BARE_IPV6_RE.test(s)) return false;
  if (s.includes('::')) return true;
  return (s.match(/:/g) || []).length === 7;
};

const parseDirectTarget = (input: string): QuickConnectTarget | null => {
  const trimmed = input.trim();
  if (!trimmed) return null;

  // Pattern: [user@]hostname[:port]
  // Hostname can be IP (v4 or v6 in brackets) or domain name
  const regex = /^(?:([^@]+)@)?([^\s:]+|\[[^\]]+\])(?::(\d+))?$/;
  const match = trimmed.match(regex);

  // If the main regex fails, try bare IPv6: [user@]ipv6_address
  // Bare IPv6 contains colons so the main regex can't distinguish host:port.
  // Port must be specified via brackets: [ipv6]:port
  if (!match) {
    const bareIpv6Regex = /^(?:([^@]+)@)?([a-fA-F0-9:]+)$/;
    const bareMatch = trimmed.match(bareIpv6Regex);
    if (bareMatch) {
      const [, bareUser, bareHost] = bareMatch;
      if (isBareIPv6(bareHost)) {
        return {
          hostname: bareHost,
          username: bareUser || undefined,
          port: undefined,
        };
      }
    }
    return null;
  }

  const [, username, hostname, portStr] = match;

  // Validate hostname looks like an IP or domain
  const ipv4Regex = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
  const ipv6Regex = /^\[?[a-fA-F0-9:]+\]?$/;
  const domainRegex =
    /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;

  if (
    !ipv4Regex.test(hostname) &&
    !ipv6Regex.test(hostname) &&
    !domainRegex.test(hostname)
  ) {
    return null;
  }

  const port = portStr ? parseInt(portStr, 10) : undefined;
  if (port !== undefined && (isNaN(port) || port < 1 || port > 65535)) {
    return null;
  }

  return {
    hostname: hostname.replace(/^\[|\]$/g, ""), // Remove IPv6 brackets
    username: username || undefined,
    port,
  };
};

const sshArgOptions = new Set([
  "-b",
  "-c",
  "-D",
  "-E",
  "-F",
  "-i",
  "-I",
  "-J",
  "-L",
  "-m",
  "-O",
  "-P",
  "-R",
  "-S",
  "-W",
  "-w",
]);

const parseSshOption = (
  raw: string,
  nextToken?: string,
): { key: string; value: string; consumedNext: boolean } | null => {
  const trimmed = raw.trim();
  if (!trimmed) return null;

  const parts = trimmed.split("=");
  if (parts.length >= 2) {
    const key = parts[0]?.trim();
    const value = parts.slice(1).join("=").trim();
    if (key && value) {
      return { key, value, consumedNext: false };
    }
  }

  if (nextToken && !nextToken.startsWith("-")) {
    return { key: trimmed, value: nextToken, consumedNext: true };
  }

  return null;
};

const parseSshCommand = (input: string): QuickConnectParseResult | null => {
  const trimmed = input.trim();
  if (!/^ssh(\s|$)/i.test(trimmed)) return null;

  const tokens = trimmed.split(/\s+/);
  if (tokens.length < 2) return null;

  const warnings: string[] = [];
  let username: string | undefined;
  let optionUsername: string | undefined;
  let port: number | undefined;
  let optionPort: number | undefined;
  let portInvalid = false;
  let optionHostname: string | undefined;
  let hostToken: string | undefined;

  for (let i = 1; i < tokens.length; i++) {
    const token = tokens[i];
    if (!token) continue;

    if (token === "-p") {
      const value = tokens[i + 1];
      if (value) {
        port = parseInt(value, 10);
        if (Number.isNaN(port)) portInvalid = true;
        i++;
      }
      continue;
    }

    if (token.startsWith("-p") && token.length > 2) {
      const value = token.replace(/^-p=?/, "");
      if (value) {
        port = parseInt(value, 10);
        if (Number.isNaN(port)) portInvalid = true;
      }
      continue;
    }

    if (token === "-l") {
      const value = tokens[i + 1];
      if (value) {
        username = value;
        i++;
      }
      continue;
    }

    if (token.startsWith("-l") && token.length > 2) {
      const value = token.replace(/^-l=?/, "");
      if (value) username = value;
      continue;
    }

    if (token === "-o") {
      const optionToken = tokens[i + 1];
      if (optionToken) {
        const nextToken = tokens[i + 2];
        const parsed = parseSshOption(optionToken, nextToken);
        if (parsed) {
          const key = parsed.key.toLowerCase();
          if (key === "port") {
            const parsedPort = parseInt(parsed.value, 10);
            if (Number.isNaN(parsedPort)) {
              portInvalid = true;
            } else {
              optionPort = parsedPort;
            }
          } else if (key === "user") {
            optionUsername = parsed.value;
          } else if (key === "hostname") {
            optionHostname = parsed.value;
          } else {
            warnings.push(`-o ${parsed.key}`);
          }
          i += parsed.consumedNext ? 2 : 1;
          continue;
        }
        warnings.push("-o");
        i++;
      }
      continue;
    }

    if (token.startsWith("-o") && token.length > 2) {
      const parsed = parseSshOption(token.slice(2), tokens[i + 1]);
      if (parsed) {
        const key = parsed.key.toLowerCase();
        if (key === "port") {
          const parsedPort = parseInt(parsed.value, 10);
          if (Number.isNaN(parsedPort)) {
            portInvalid = true;
          } else {
            optionPort = parsedPort;
          }
        } else if (key === "user") {
          optionUsername = parsed.value;
        } else if (key === "hostname") {
          optionHostname = parsed.value;
        } else {
          warnings.push(`-o ${parsed.key}`);
        }
        if (parsed.consumedNext) i++;
        continue;
      }
      warnings.push("-o");
    }

    if (sshArgOptions.has(token)) {
      warnings.push(token);
      const next = tokens[i + 1];
      if (next) i++;
      continue;
    }

    if (token.startsWith("-")) {
      warnings.push(token);
      continue;
    }

    if (!hostToken) {
      hostToken = token;
    } else {
      warnings.push(token);
    }
  }

  if (!hostToken) return null;

  const base = optionHostname
    ? parseDirectTarget(optionHostname)
    : parseDirectTarget(hostToken);
  if (!base) return null;

  if (portInvalid) return null;

  const resolvedPort =
    port !== undefined && !Number.isNaN(port)
      ? port
      : optionPort !== undefined && !Number.isNaN(optionPort)
        ? optionPort
        : base.port;
  if (
    resolvedPort !== undefined &&
    (Number.isNaN(resolvedPort) || resolvedPort < 1 || resolvedPort > 65535)
  ) {
    return null;
  }

  return {
    target: {
      hostname: base.hostname,
      username: optionUsername || username || base.username,
      port: resolvedPort,
    },
    warnings: Array.from(new Set(warnings)),
  };
};

// Parse user@host:port or ssh command formats with warning details
export function parseQuickConnectInputWithWarnings(
  input: string,
): QuickConnectParseResult {
  const trimmed = input.trim();
  if (!trimmed) return { target: null, warnings: [] };

  const sshTarget = parseSshCommand(trimmed);
  if (sshTarget) return sshTarget;

  return { target: parseDirectTarget(trimmed), warnings: [] };
}

// Parse user@host:port or ssh command formats
export function parseQuickConnectInput(
  input: string,
): QuickConnectTarget | null {
  return parseQuickConnectInputWithWarnings(input).target;
}

// Check if input looks like a quick connect address
export function isQuickConnectInput(input: string): boolean {
  return parseQuickConnectInput(input) !== null;
}
