// nodes_provider_test.dart — 节点选择 / 智能选择派生
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pangolin_vpn/l10n/app_text.dart';
import 'package:pangolin_vpn/models/node.dart';
import 'package:pangolin_vpn/services/token_store.dart';
import 'package:pangolin_vpn/state/auth_provider.dart';
import 'package:pangolin_vpn/state/nodes_provider.dart';

// ── Stub TokenStore（无平台依赖）──────────────────────────────────

class _NullTokenStore implements TokenStore {
  const _NullTokenStore();
  @override
  Future<void> saveTokens({required String access, required String refresh}) async {}
  @override
  Future<String?> loadAccessToken() async => null;
  @override
  Future<String?> loadRefreshToken() async => null;
  @override
  Future<void> clear() async {}
  @override
  Future<void> markOnboarded() async {}
  @override
  Future<bool> isOnboarded() async => true;
  @override
  Future<void> saveLastEmail(String email) async {}
  @override
  Future<String?> loadLastEmail() async => null;
}

// 直接喂固定节点列表(不触发真实探针/HTTP)。
class _FakeNodes extends NodesNotifier {
  _FakeNodes(this.list);
  final List<Node> list;
  @override
  Future<List<Node>> build() async => list;
}

const _nodes = [
  Node(code: 'HK', nameZh: '香港', nameEn: 'Hong Kong', ping: 30, uuid: 'hk'),
  Node(code: 'JP', nameZh: '日本', nameEn: 'Tokyo', ping: 12, uuid: 'jp'),
  Node(code: 'US', nameZh: '美国', nameEn: 'LA', ping: 200, uuid: 'us'),
];

ProviderContainer makeContainer({List<Override> overrides = const []}) =>
    ProviderContainer(overrides: [
      tokenStoreProvider.overrideWithValue(const _NullTokenStore()),
      ...overrides,
    ]);

void main() {
  test('默认智能选择(AUTO)', () {
    final c = makeContainer();
    addTearDown(c.dispose);
    expect(c.read(selectedNodeCodeProvider), kSmartNodeCode);
    expect(c.read(isSmartSelectProvider), true);
  });

  test('无节点时 effectiveNode 返回占位(不再伪造演示节点)', () {
    final c = makeContainer();
    addTearDown(c.dispose);
    expect(c.read(effectiveNodeProvider).code, kPlaceholderNode.code);
  });

  test('智能选择取实测延迟最小节点', () async {
    final c = makeContainer(overrides: [nodesProvider.overrideWith(() => _FakeNodes(_nodes))]);
    addTearDown(c.dispose);
    await c.read(nodesProvider.future);
    expect(c.read(effectiveNodeProvider).code, 'JP'); // ping 12 最小
  });

  test('选中具体节点后生效节点随之改变', () async {
    final c = makeContainer(overrides: [nodesProvider.overrideWith(() => _FakeNodes(_nodes))]);
    addTearDown(c.dispose);
    await c.read(nodesProvider.future);
    c.read(selectedNodeCodeProvider.notifier).state = 'US';
    expect(c.read(isSmartSelectProvider), false);
    expect(c.read(effectiveNodeProvider).code, 'US');
  });

  test('pingLabel:未测得显示 —', () {
    expect(const Node(code: 'X', nameZh: 'x', nameEn: 'x', ping: 0).pingLabel, '—');
    expect(const Node(code: 'X', nameZh: 'x', nameEn: 'x', ping: 42).pingLabel, '42ms');
  });

  test('localizedSub 不串语言:无标签用拉丁地名', () {
    const tw = Node(code: 'TW', nameZh: '台湾 台北', nameEn: 'Taipei', ping: 28);
    expect(tw.localizedSub(AppLang.zh), 'Taipei');
    expect(tw.localizedSub(AppLang.en), 'Taipei');
    const hk = Node(code: 'HK', nameZh: '香港 · 流媒体', nameEn: 'Hong Kong', ping: 18, tag: NodeTag.streaming);
    expect(hk.localizedSub(AppLang.zh), '流媒体优化');
    expect(hk.localizedSub(AppLang.en), 'Streaming');
  });
}
