import 'package:flutter_test/flutter_test.dart';
import 'package:komet/core/links/max_link.dart';
import 'package:komet/frontend/widgets/link_text.dart';

void main() {
  group('linkPattern', () {
    List<String> matches(String text) =>
        linkPattern.allMatches(text).map((m) => m.group(0)!).toList();

    test('picks up a bare max.ru link inside plain text', () {
      expect(
        matches('Ваша ссылка 👇\nmax.ru/id100000000001_bot?start=abc123\n'),
        ['max.ru/id100000000001_bot?start=abc123'],
      );
      expect(matches('зайди на max.ru и посмотри'), ['max.ru']);
    });

    test('still picks up schemed and www links', () {
      expect(matches('https://max.ru/somebot?start=x'), [
        'https://max.ru/somebot?start=x',
      ]);
      expect(matches('www.max.ru/somebot'), ['www.max.ru/somebot']);
    });

    test('does not match look-alike hosts or emails', () {
      expect(matches('evil.max.ru/phish'), isEmpty);
      expect(matches('max.ru.evil.com/phish'), isEmpty);
      expect(matches('bot@max.ru'), isEmpty);
      expect(matches('max.rules/somebot'), isEmpty);
    });

    test('linkTarget adds a scheme only when missing', () {
      expect(linkTarget('max.ru/somebot'), 'https://max.ru/somebot');
      expect(linkTarget('www.max.ru/somebot'), 'https://www.max.ru/somebot');
      expect(linkTarget('http://max.ru/somebot'), 'http://max.ru/somebot');
      expect(linkTarget('https://max.ru/somebot'), 'https://max.ru/somebot');
    });
  });

  group('MaxLink.parse — не наши ссылки', () {
    test('rejects other hosts and non-links', () {
      expect(MaxLink.parse('https://example.com/somebot'), isNull);
      expect(MaxLink.parse('evil.max.ru/phish'), isNull);
      expect(MaxLink.parse('mailto:bot@max.ru'), isNull);
      expect(MaxLink.parse(''), isNull);
    });

    test('rejects reserved site pages', () {
      expect(MaxLink.parse('https://max.ru/login'), isNull);
      expect(MaxLink.parse('https://max.ru/tos'), isNull);
    });
  });

  group('MaxLink.parse — контентные ссылки', () {
    test('bare host, http and www all normalize to the root link', () {
      expect(MaxLink.parse('max.ru'), isA<MaxRootLink>());
      expect(MaxLink.parse('http://max.ru/'), isA<MaxRootLink>());
      expect(MaxLink.parse('https://www.max.ru'), isA<MaxRootLink>());
      expect(MaxLink.parse('max://max.ru/'), isA<MaxRootLink>());
    });

    test('public link keeps the canonical https url', () {
      final link = MaxLink.parse('max.ru/somebot') as MaxContentLink;

      expect(link.kind, MaxContentKind.public);
      expect(link.url, 'https://max.ru/somebot');
      expect(link.baseUrl, 'https://max.ru/somebot');
      expect(link.startPayload, isNull);
      expect(link.messageId, isNull);
    });

    test('@nickname is a public link too', () {
      final link = MaxLink.parse('https://max.ru/@somebot') as MaxContentLink;

      expect(link.kind, MaxContentKind.public);
      expect(link.baseUrl, 'https://max.ru/somebot');
    });

    test('bot start payload is parsed and the base url drops it', () {
      final link =
          MaxLink.parse('http://max.ru/id100000000001_bot?start=a%20b')
              as MaxContentLink;

      expect(link.startPayload, 'a b');
      expect(link.url, 'https://max.ru/id100000000001_bot?start=a%20b');
      expect(link.baseUrl, 'https://max.ru/id100000000001_bot');
    });

    test('empty start payload is ignored', () {
      final link = MaxLink.parse('max.ru/somebot?start=') as MaxContentLink;
      expect(link.startPayload, isNull);
    });

    test('startapp opens a mini app and is cut at the first &', () {
      final link =
          MaxLink.parse('max.ru/somebot?startapp=deal%2F42&ref=x')
              as MaxWebAppLink;

      expect(link.startApp, 'deal/42');
    });

    test('message links carry the message id', () {
      final byName =
          MaxLink.parse('max.ru/somechannel/117008613873053494')
              as MaxContentLink;
      expect(byName.kind, MaxContentKind.public);
      expect(byName.messageId, 117008613873053494);
      expect(byName.baseUrl, 'https://max.ru/somechannel');

      final byId =
          MaxLink.parse('max.ru/c/1673760/117008613873053494')
              as MaxContentLink;
      expect(byId.kind, MaxContentKind.content);
      expect(byId.messageId, 117008613873053494);
      expect(byId.baseUrl, 'https://max.ru/c/1673760');
    });

    test('invite, call and sticker links keep their own types', () {
      expect(
        (MaxLink.parse('max.ru/join/AbCdEf') as MaxContentLink).kind,
        MaxContentKind.invite,
      );
      expect(MaxLink.parse('max.ru/joincall/AbCdEf'), isA<MaxCallLink>());
      expect(
        (MaxLink.parse('max.ru/stickerset/512-abc') as MaxStickerSetLink).path,
        'stickerset/512-abc',
      );
    });

    test('uid and cid open a contact and a chat', () {
      expect(
        (MaxLink.parse('max://max.ru/?uid=105587131') as MaxContactIdLink)
            .userId,
        105587131,
      );
      final chat = MaxLink.parse('max://max.ru/?cid=1673760') as MaxChatIdLink;
      expect(chat.chatId, 1673760);
      expect(chat.messageId, isNull);
    });
  });

  group('MaxLink.parse — внутренние маршруты', () {
    test('auth keeps the full url with its token', () {
      final link = MaxLink.parse('https://max.ru/:auth/tok3n') as MaxAuthLink;
      expect(link.url, 'https://max.ru/:auth/tok3n');
    });

    test('share and share-self-out are separate targets', () {
      final share =
          MaxLink.parse('https://max.ru/:share?text=%D0%BF%D1%80%D0%B8%D0%B2')
              as MaxShareTextLink;
      expect(share.text, 'прив');
      expect(
        MaxLink.parse('https://max.ru/:share-self-out'),
        isA<MaxShareSelfLink>(),
      );
    });

    test('folder needs an id, otherwise it stays a plain route', () {
      expect(
        (MaxLink.parse('max.ru/:folder?id=42') as MaxFolderLink).folderId,
        '42',
      );
      expect(MaxLink.parse('max.ru/:folder'), isA<MaxRouteLink>());
    });

    test('current is a no-op target', () {
      expect(MaxLink.parse('max.ru/:current'), isA<MaxCurrentLink>());
    });

    test('other routes keep their path and query params', () {
      final route =
          MaxLink.parse('max://max.ru/:profile?id=123&type=CHAT')
              as MaxRouteLink;

      expect(route.route, ':profile');
      expect(route.params, {'id': '123', 'type': 'CHAT'});

      final nested =
          MaxLink.parse('https://max.ru/:Settings/Appearance') as MaxRouteLink;
      expect(nested.route, ':settings/appearance');
    });
  });
}
