import 'package:flutter_test/flutter_test.dart';
import 'package:showen_v2_flutter/models/api_response.dart';
import 'package:showen_v2_flutter/models/app_event.dart';
import 'package:showen_v2_flutter/models/ble_models.dart';
import 'package:showen_v2_flutter/models/ble_status.dart';
import 'package:showen_v2_flutter/models/device_status.dart';
import 'package:showen_v2_flutter/models/player_status.dart';
import 'package:showen_v2_flutter/models/video_item.dart';
import 'package:showen_v2_flutter/models/wifi_network.dart';
import 'package:showen_v2_flutter/models/wifi_status.dart';

void main() {
  group('ApiResponse', () {
    test('fromJson and toJson round trip', () {
      final response = ApiResponse.fromJson(const <String, dynamic>{
        'status': 'ok',
        'message': 'done',
      });

      expect(response.isOk, isTrue);
      expect(response.toJson(), <String, dynamic>{
        'status': 'ok',
        'message': 'done',
      });
    });
  });

  group('AppEvent', () {
    test('fromJson prefers data payload map', () {
      final event = AppEvent.fromJson(const <String, dynamic>{
        'type': 'status',
        'data': <String, dynamic>{'connected': true},
      });

      expect(event.type, 'status');
      expect(event.payload, <String, dynamic>{'connected': true});
    });

    test('fromJson normalizes scalar payload', () {
      final event = AppEvent.fromJson(const <String, dynamic>{
        'type': 'progress',
        'payload': 42,
      });

      expect(event.payload, <String, dynamic>{'value': 42});
    });
  });

  group('Ble models', () {
    test('BleDevice stores constructor fields', () {
      const device = BleDevice(name: 'Showen', id: 'dev-1', rssi: -48);

      expect(device.name, 'Showen');
      expect(device.id, 'dev-1');
      expect(device.rssi, -48);
    });

    test('BleStatus parses json and raw json', () {
      final status = BleStatus.fromJson(const <String, dynamic>{
        'ok': true,
        'action': 'provision',
        'state': 'queued',
      });
      final raw = BleStatus.fromRawJson(
        '{"ok":false,"action":"scan","error":"failed"}',
      );

      expect(status.isQueued, isTrue);
      expect(status.message, 'queued');
      expect(raw.isSuccess, isFalse);
      expect(raw.message, 'failed');
    });
  });

  group('BleServiceStatus', () {
    test('initial and fromJson', () {
      final initial = BleServiceStatus.initial();
      final status = BleServiceStatus.fromJson(const <String, dynamic>{
        'running': true,
        'embedded': true,
        'device_name': 'Showen BLE',
      });

      expect(initial.running, isFalse);
      expect(initial.embedded, isFalse);
      expect(status.running, isTrue);
      expect(status.embedded, isTrue);
      expect(status.deviceName, 'Showen BLE');
    });
  });

  group('DeviceStatus', () {
    test('initial and copyWith preserve nested models', () {
      final updated = DeviceStatus.initial().copyWith(
        connected: true,
        connectionType: 'wifi',
        deviceName: 'Showen Box',
        ipAddress: '192.168.1.20',
        playerStatus: PlayerStatus.initial().copyWith(running: true),
        wifiStatus: WifiStatus.fromJson(
          const <String, dynamic>{'connected': true, 'ssid': 'Office'},
        ),
        bleStatus: BleServiceStatus.fromJson(
          const <String, dynamic>{'running': true, 'embedded': false},
        ),
      );

      expect(updated.connected, isTrue);
      expect(updated.connectionType, 'wifi');
      expect(updated.deviceName, 'Showen Box');
      expect(updated.ipAddress, '192.168.1.20');
      expect(updated.playerStatus?.running, isTrue);
      expect(updated.wifiStatus?.ssid, 'Office');
      expect(updated.bleStatus?.running, isTrue);
    });
  });

  group('PlayerStatus', () {
    test('fromJson and toJson round trip', () {
      final status = PlayerStatus.fromJson(const <String, dynamic>{
        'running': true,
        'paused': false,
        'in_transition': true,
        'current_index': 3,
        'playlist_length': 9,
        'current_video': 'intro.mp4',
      });

      expect(status.toJson(), <String, dynamic>{
        'running': true,
        'paused': false,
        'in_transition': true,
        'current_index': 3,
        'playlist_length': 9,
        'current_video': 'intro.mp4',
      });
      expect(status.copyWith(paused: true).paused, isTrue);
    });
  });

  group('VideoItem', () {
    test('fromJson parses file metadata', () {
      final video = VideoItem.fromJson(const <String, dynamic>{
        'name': 'demo.mp4',
        'size': 3145728,
      });

      expect(video.name, 'demo.mp4');
      expect(video.size, 3145728);
      expect(video.sizeLabel, '3.0 MB');
    });
  });

  group('WifiNetwork', () {
    test('fromJson parses network metadata', () {
      final network = WifiNetwork.fromJson(const <String, dynamic>{
        'ssid': 'ShowenLab',
        'signal': -51,
        'security': 'WPA2',
      });

      expect(network.ssid, 'ShowenLab');
      expect(network.signalLabel, '-51 dBm');
      expect(network.security, 'WPA2');
    });
  });

  group('WifiStatus', () {
    test('disconnected and fromJson', () {
      final disconnected = WifiStatus.disconnected();
      final status = WifiStatus.fromJson(const <String, dynamic>{
        'connected': true,
        'ssid': 'ShowenLab',
        'ip': '192.168.1.10',
      });

      expect(disconnected.connected, isFalse);
      expect(status.connected, isTrue);
      expect(status.ssid, 'ShowenLab');
      expect(status.ip, '192.168.1.10');
    });
  });
}
