import 'package:flutter_test/flutter_test.dart';

import 'package:chanora_flutter/services/snapshot_state_mapper.dart';
import 'package:chanora_flutter/src/rust/api.dart' as rust;

void main() {
  rust.BridgeChannel channel({int neededTalkPower = 0}) {
    return rust.BridgeChannel(
      id: BigInt.one,
      parent: BigInt.zero,
      name: 'Lobby',
      order: 0,
      hasPassword: false,
      neededTalkPower: neededTalkPower,
    );
  }

  rust.BridgeClient client({
    BigInt? id,
    BigInt? channelId,
    int talkPower = 0,
    bool talkPowerGranted = false,
    bool inputMuted = false,
    bool outputMuted = false,
  }) {
    return rust.BridgeClient(
      id: id ?? BigInt.from(7),
      channel: channelId ?? BigInt.one,
      name: 'Me',
      inputMuted: inputMuted,
      outputMuted: outputMuted,
      isSpeaking: false,
      isServerQuery: false,
      talkPower: talkPower,
      talkPowerGranted: talkPowerGranted,
    );
  }

  rust.BridgeSnapshot snapshot({
    required List<rust.BridgeChannel> channels,
    required List<rust.BridgeClient> clients,
    BigInt? ownClientId,
  }) {
    return rust.BridgeSnapshot(
      serverName: 'Server',
      welcomeMessage: '',
      platform: '',
      version: '',
      channels: channels,
      clients: clients,
      ownClientId: ownClientId ?? BigInt.from(7),
    );
  }

  test('extracts own channel and mute state', () {
    final state = ownClientSnapshotState(
      snapshot(
        channels: [channel()],
        clients: [client(inputMuted: true, outputMuted: true)],
      ),
    );

    expect(state?.channelId, BigInt.one);
    expect(state?.inputMuted, isTrue);
    expect(state?.outputMuted, isTrue);
    expect(state?.talkPowerOk, isTrue);
    expect(state?.talkPower, 0);
    expect(state?.talkPowerGranted, isFalse);
    expect(state?.neededTalkPower, 0);
  });

  test('detects insufficient talk power', () {
    final state = ownClientSnapshotState(
      snapshot(
        channels: [channel(neededTalkPower: 20)],
        clients: [client(talkPower: 10)],
      ),
    );

    expect(state?.talkPowerOk, isFalse);
    expect(state?.talkPower, 10);
    expect(state?.neededTalkPower, 20);
  });

  test('accepts granted or sufficient talk power', () {
    final sufficient = ownClientSnapshotState(
      snapshot(
        channels: [channel(neededTalkPower: 20)],
        clients: [client(talkPower: 20)],
      ),
    );
    final granted = ownClientSnapshotState(
      snapshot(
        channels: [channel(neededTalkPower: 20)],
        clients: [client(talkPowerGranted: true)],
      ),
    );

    expect(sufficient?.talkPowerOk, isTrue);
    expect(granted?.talkPowerOk, isTrue);
  });

  test('returns null when own client is absent', () {
    final state = ownClientSnapshotState(
      snapshot(
        channels: [channel()],
        clients: [client(id: BigInt.from(9))],
      ),
    );

    expect(state, isNull);
  });

  test('resolves channel name by id', () {
    final snap = snapshot(
      channels: [
        channel(),
        rust.BridgeChannel(
          id: BigInt.from(2),
          parent: BigInt.zero,
          name: 'Raid Room',
          order: 1,
          hasPassword: false,
          neededTalkPower: 0,
        ),
      ],
      clients: [client()],
    );

    expect(snapshotChannelName(snap, BigInt.from(2)), 'Raid Room');
  });

  test('returns empty channel name when snapshot or channel is absent', () {
    final snap = snapshot(channels: [channel()], clients: [client()]);

    expect(snapshotChannelName(null, BigInt.one), isEmpty);
    expect(snapshotChannelName(snap, null), isEmpty);
    expect(snapshotChannelName(snap, BigInt.from(99)), isEmpty);
  });

  test('resolves needed talk power by channel id', () {
    final snap = snapshot(
      channels: [channel(neededTalkPower: 30)],
      clients: [client()],
    );

    expect(snapshotNeededTalkPower(snap, BigInt.one), 30);
    expect(snapshotNeededTalkPower(null, BigInt.one), isNull);
    expect(snapshotNeededTalkPower(snap, null), isNull);
    expect(snapshotNeededTalkPower(snap, BigInt.from(99)), isNull);
  });
}
