import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/device.dart';
import '../models/user_info.dart';
import '../models/static_talkgroup.dart';
import '../models/device_profile.dart';

class BrandmeisterError implements Exception {
  final String message;
  final int? statusCode;

  BrandmeisterError(this.message, {this.statusCode});

  @override
  String toString() {
    if (statusCode != null) {
      return 'BrandmeisterError (HTTP $statusCode): $message';
    }
    return 'BrandmeisterError: $message';
  }
}

class BrandmeisterClient {
  static const String baseUrl = 'https://api.brandmeister.network/v2';
  final String apiToken;

  BrandmeisterClient({required this.apiToken});

  Map<String, String> get _headers => {
        'Authorization': 'Bearer $apiToken',
        'Content-Type': 'application/json',
      };

  Future<T> _handleResponse<T>(
    http.Response response,
    T Function(dynamic) parser,
  ) async {
    if (response.statusCode >= 200 && response.statusCode < 300) {
      try {
        final dynamic decoded = jsonDecode(response.body);
        return parser(decoded);
      } catch (e) {
        throw BrandmeisterError('Failed to decode response: $e');
      }
    } else {
      throw BrandmeisterError(
        'HTTP request failed: ${response.body}',
        statusCode: response.statusCode,
      );
    }
  }

  // User Endpoints
  Future<UserInfo> whoami() async {
    final response = await http.post(
      Uri.parse('$baseUrl/user/whoAmI'),
      headers: _headers,
    );
    return _handleResponse(response, (json) => UserInfo.fromJson(json));
  }

  Future<UserInfo> getUser(int id) async {
    final response = await http.get(
      Uri.parse('$baseUrl/user/byID/$id'),
      headers: _headers,
    );
    return _handleResponse(response, (json) => UserInfo.fromJson(json));
  }

  Future<List<UserInfo>> getUsersByCallsign(String callsign) async {
    final response = await http.get(
      Uri.parse('$baseUrl/user/byCall/$callsign'),
      headers: _headers,
    );
    return _handleResponse(
      response,
      (json) => (json as List).map((e) => UserInfo.fromJson(e)).toList(),
    );
  }

  // Device Endpoints
  Future<Device> getDevice(int dmrId) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/$dmrId'),
      headers: _headers,
    );
    return _handleResponse(response, (json) => Device.fromJson(json));
  }

  Future<DeviceProfile> getDeviceProfile(int dmrId) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/$dmrId/profile'),
      headers: _headers,
    );
    return _handleResponse(response, (json) => DeviceProfile.fromJson(json));
  }

  Future<List<Device>> getDevicesByCallsign(String callsign) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/byCall?callsign=$callsign'),
      headers: _headers,
    );
    return _handleResponse(
      response,
      (json) => (json as List).map((e) => Device.fromJson(e)).toList(),
    );
  }

  Future<List<Device>> getDevicesByMaster(int masterId) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/byMaster/$masterId'),
      headers: _headers,
    );
    return _handleResponse(
      response,
      (json) => (json as List).map((e) => Device.fromJson(e)).toList(),
    );
  }

  Future<List<StaticTalkgroup>> getDeviceTalkgroups(int dmrId) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/$dmrId/talkgroup'),
      headers: _headers,
    );
    return _handleResponse(
      response,
      (json) =>
          (json as List).map((e) => StaticTalkgroup.fromJson(e)).toList(),
    );
  }

  // Talkgroup Management
  Future<void> addTalkgroup({
    required int dmrId,
    required int talkgroupId,
    required int slot,
  }) async {
    final response = await http.post(
      Uri.parse('$baseUrl/device/$dmrId/talkgroup'),
      headers: _headers,
      body: jsonEncode({
        'slot': slot,
        'group': talkgroupId,
      }),
    );

    if (response.statusCode < 200 || response.statusCode >= 300) {
      throw BrandmeisterError(
        'Failed to add talkgroup: ${response.body}',
        statusCode: response.statusCode,
      );
    }
  }

  Future<void> removeTalkgroup({
    required int dmrId,
    required int talkgroupId,
    required int slot,
  }) async {
    final response = await http.delete(
      Uri.parse('$baseUrl/device/$dmrId/talkgroup/$slot/$talkgroupId'),
      headers: _headers,
    );

    if (response.statusCode < 200 || response.statusCode >= 300) {
      throw BrandmeisterError(
        'Failed to remove talkgroup: ${response.body}',
        statusCode: response.statusCode,
      );
    }
  }

  Future<void> dropAutoStaticGroup(int dmrId) async {
    final response = await http.get(
      Uri.parse('$baseUrl/device/$dmrId/action/dropAutoStaticGroup'),
      headers: _headers,
    );

    if (response.statusCode < 200 || response.statusCode >= 300) {
      throw BrandmeisterError(
        'Failed to drop auto-static group: ${response.body}',
        statusCode: response.statusCode,
      );
    }
  }

  // Talkgroup Endpoints
  Future<Map<String, String>> getTalkgroups() async {
    final response = await http.get(
      Uri.parse('$baseUrl/talkgroup'),
      headers: _headers,
    );
    return _handleResponse(
      response,
      (json) => (json as Map<String, dynamic>).map(
        (key, value) => MapEntry(key, value.toString()),
      ),
    );
  }
}
