typedef Ts3ServerLinkHandler = Future<void> Function(Ts3ServerLink link);

class Ts3ServerLink {
  const Ts3ServerLink({
    required this.host,
    required this.hostWithPort,
    this.port,
    this.nickname,
    this.password,
    this.channel,
    this.cid,
    this.channelPassword,
    this.token,
    this.addBookmark,
  });

  final String host;
  final String hostWithPort;
  final int? port;
  final String? nickname;
  final String? password;
  final String? channel;
  final String? cid;
  final String? channelPassword;
  final String? token;
  final String? addBookmark;
}

Ts3ServerLink? parseTs3ServerLink(String rawUrl) {
  final trimmed = rawUrl.trim();
  if (!trimmed.toLowerCase().startsWith('ts3server://')) return null;

  var body = trimmed.substring('ts3server://'.length);
  body = body.replaceFirst(RegExp(r'/+$'), '');
  if (body.isEmpty) return null;

  final split = _splitAuthorityAndQuery(body);
  final hostPart = Uri.decodeComponent(split.authority).trim();
  if (hostPart.isEmpty) return null;

  final params = split.query == null
      ? const <String, String>{}
      : Uri.splitQueryString(split.query!);
  final port = int.tryParse(params['port'] ?? '');
  final hostWithPort = port == null || _hasExplicitPort(hostPart)
      ? hostPart
      : '$hostPart:$port';

  return Ts3ServerLink(
    host: hostPart,
    hostWithPort: hostWithPort,
    port: port,
    nickname: _emptyToNull(params['nickname']),
    password: _emptyToNull(params['password']),
    channel: _emptyToNull(params['channel']),
    cid: _emptyToNull(params['cid']),
    channelPassword: _emptyToNull(params['channelpassword']),
    token: _emptyToNull(params['token']),
    addBookmark: _emptyToNull(params['addbookmark']),
  );
}

({String authority, String? query}) _splitAuthorityAndQuery(String body) {
  final rawQueryIndex = body.indexOf('?');
  if (rawQueryIndex >= 0) {
    return (
      authority: body.substring(0, rawQueryIndex),
      query: body.substring(rawQueryIndex + 1),
    );
  }

  final encodedQueryIndex = body.toLowerCase().indexOf('%3f');
  if (encodedQueryIndex >= 0) {
    return (
      authority: body.substring(0, encodedQueryIndex),
      query: Uri.decodeComponent(body.substring(encodedQueryIndex + 3)),
    );
  }

  return (authority: body, query: null);
}

bool _hasExplicitPort(String host) {
  final lastColon = host.lastIndexOf(':');
  if (lastColon <= 0 || lastColon == host.length - 1) return false;
  return int.tryParse(host.substring(lastColon + 1)) != null;
}

String? _emptyToNull(String? value) {
  if (value == null || value.isEmpty) return null;
  return value;
}
