enum MaxContentKind { public, invite, user, content }

sealed class MaxLink {
  const MaxLink();

  bool get needsConnection => false;

  static final RegExp _schemeless = RegExp(
    r'^(?:www\.)?max\.ru(?:[/?#]|$)',
    caseSensitive: false,
  );

  static final RegExp _segment = RegExp(r'^[A-Za-z0-9_]+$');

  static const Set<String> _reserved = {
    'login',
    'ps',
    'tos',
    'privacy',
    'about',
    'help',
  };

  static bool isMaxLink(String url) => parse(url) != null;

  static MaxLink? parse(String input) {
    final uri = _canonical(input);
    if (uri == null) return null;

    final segments = uri.pathSegments
        .where((s) => s.isNotEmpty)
        .toList(growable: false);
    final params = uri.queryParameters;
    final url = uri.replace(scheme: 'https', host: 'max.ru').toString();

    if (segments.isEmpty) return _parseQueryOnly(params);
    if (segments.first.startsWith(':')) {
      return _parseRoute(segments, params, url);
    }
    return _parseContent(segments, params, url);
  }

  static Uri? _canonical(String input) {
    var value = input.trim();
    if (value.isEmpty) return null;
    if (!value.contains('://')) {
      if (!_schemeless.hasMatch(value)) return null;
      value = 'https://$value';
    }
    final uri = Uri.tryParse(value);
    if (uri == null) return null;
    final scheme = uri.scheme.toLowerCase();
    if (scheme != 'https' && scheme != 'http' && scheme != 'max') return null;
    final host = uri.host.toLowerCase();
    if (host != 'max.ru' && host != 'www.max.ru') return null;
    return uri;
  }

  static MaxLink _parseQueryOnly(Map<String, String> params) {
    final userId = _idParam(params, 'uid');
    if (userId != null) return MaxContactIdLink(userId);
    final chatId = _idParam(params, 'cid');
    if (chatId != null) return MaxChatIdLink(chatId);
    return const MaxRootLink();
  }

  static MaxLink _parseRoute(
    List<String> segments,
    Map<String, String> params,
    String url,
  ) {
    final route = segments.join('/').toLowerCase();
    switch (route) {
      case ':auth':
        return MaxAuthLink(url);
      case ':current':
        return const MaxCurrentLink();
      case ':share-self-out':
        return const MaxShareSelfLink();
      case ':share':
        return MaxShareTextLink(params['text']?.trim() ?? '');
      case ':folder':
        final id = params['id']?.trim();
        if (id != null && id.isNotEmpty) return MaxFolderLink(id);
    }
    if (segments.length > 1 && segments.first.toLowerCase() == ':auth') {
      return MaxAuthLink(url);
    }
    return MaxRouteLink(route, params);
  }

  static MaxLink? _parseContent(
    List<String> segments,
    Map<String, String> params,
    String url,
  ) {
    final first = segments.first;
    final lower = first.toLowerCase();

    if (segments.length == 1) {
      final name = _publicName(first);
      if (name == null) return null;
      final startApp = params['startapp'] ?? params['startApp'];
      if (startApp != null && startApp.isNotEmpty) {
        return MaxWebAppLink('https://max.ru/$name', startApp.split('&').first);
      }
      return MaxContentLink(
        kind: MaxContentKind.public,
        url: url,
        baseUrl: 'https://max.ru/$name',
        startPayload: _startPayload(params),
      );
    }

    if (segments.length == 2) {
      switch (lower) {
        case 'stickerset':
          return MaxStickerSetLink('$lower/${segments[1]}');
        case 'joincall':
          return MaxCallLink(url);
        case 'join':
          return MaxContentLink(
            kind: MaxContentKind.invite,
            url: url,
            baseUrl: url,
          );
        case 'u':
          return MaxContentLink(
            kind: MaxContentKind.user,
            url: url,
            baseUrl: url,
          );
      }
      final messageId = int.tryParse(segments[1]);
      final name = _publicName(first);
      if (messageId == null || name == null) return null;
      return MaxContentLink(
        kind: MaxContentKind.public,
        url: url,
        baseUrl: 'https://max.ru/$name',
        messageId: messageId,
      );
    }

    if (lower == 'c' && segments.length == 3) {
      final chatId = int.tryParse(segments[1]);
      final messageId = int.tryParse(segments[2]);
      if (chatId == null || messageId == null) return null;
      return MaxContentLink(
        kind: MaxContentKind.content,
        url: url,
        baseUrl: 'https://max.ru/c/$chatId',
        messageId: messageId,
      );
    }

    if (lower == 'join') {
      return MaxContentLink(
        kind: MaxContentKind.invite,
        url: url,
        baseUrl: url,
      );
    }
    return null;
  }

  static String? _publicName(String segment) {
    final name = segment.startsWith('@') ? segment.substring(1) : segment;
    if (name.isEmpty) return null;
    if (_reserved.contains(name.toLowerCase())) return null;
    if (!_segment.hasMatch(name)) return null;
    return name;
  }

  static String? _startPayload(Map<String, String> params) {
    final value = params['start']?.trim();
    return (value == null || value.isEmpty) ? null : value;
  }

  static int? _idParam(Map<String, String> params, String key) {
    final raw = params[key]?.trim();
    if (raw == null || raw.isEmpty) return null;
    final value = int.tryParse(raw);
    return (value == null || value <= 0) ? null : value;
  }
}

class MaxRootLink extends MaxLink {
  const MaxRootLink();
}

class MaxCurrentLink extends MaxLink {
  const MaxCurrentLink();
}

class MaxShareSelfLink extends MaxLink {
  const MaxShareSelfLink();
}

class MaxAuthLink extends MaxLink {
  final String url;

  const MaxAuthLink(this.url);

  @override
  bool get needsConnection => true;
}

class MaxCallLink extends MaxLink {
  final String url;

  const MaxCallLink(this.url);

  @override
  bool get needsConnection => true;
}

class MaxStickerSetLink extends MaxLink {
  final String path;

  const MaxStickerSetLink(this.path);

  @override
  bool get needsConnection => true;
}

class MaxShareTextLink extends MaxLink {
  final String text;

  const MaxShareTextLink(this.text);
}

class MaxFolderLink extends MaxLink {
  final String folderId;

  const MaxFolderLink(this.folderId);
}

class MaxRouteLink extends MaxLink {
  final String route;
  final Map<String, String> params;

  const MaxRouteLink(this.route, this.params);
}

class MaxContactIdLink extends MaxLink {
  final int userId;

  const MaxContactIdLink(this.userId);

  @override
  bool get needsConnection => true;
}

class MaxChatIdLink extends MaxLink {
  final int chatId;
  final int? messageId;

  const MaxChatIdLink(this.chatId, {this.messageId});
}

class MaxWebAppLink extends MaxLink {
  final String url;
  final String startApp;

  const MaxWebAppLink(this.url, this.startApp);

  @override
  bool get needsConnection => true;
}

class MaxContentLink extends MaxLink {
  final MaxContentKind kind;
  final String url;
  final String baseUrl;
  final String? startPayload;
  final int? messageId;

  const MaxContentLink({
    required this.kind,
    required this.url,
    required this.baseUrl,
    this.startPayload,
    this.messageId,
  });

  @override
  bool get needsConnection => true;
}
