import 'dart:convert';

import 'package:http/http.dart' as http;

import '../config/api_config.dart';

/// Un partecipante a una classe (vista insegnante).
class TeacherParticipant {
  final int bookingId;
  final String fullName;
  final bool isLost;

  const TeacherParticipant({
    required this.bookingId,
    required this.fullName,
    required this.isLost,
  });

  factory TeacherParticipant.fromMap(Map<String, dynamic> m) =>
      TeacherParticipant(
        bookingId: (m['booking_id'] as num?)?.toInt() ?? 0,
        fullName: (m['full_name'] ?? '').toString().trim(),
        isLost: m['is_lost'] == true,
      );
}

/// Una classe programmata con i suoi partecipanti.
class TeacherClass {
  final int scheduleId;
  final int serviceId;
  final String className;
  final String? colorClass;
  final String dateTime; // "YYYY-MM-DD HH:MM:SS"
  final int maxCapacity;
  final int bookedCount;
  final bool isFull;
  final List<TeacherParticipant> participants;

  const TeacherClass({
    required this.scheduleId,
    required this.serviceId,
    required this.className,
    required this.colorClass,
    required this.dateTime,
    required this.maxCapacity,
    required this.bookedCount,
    required this.isFull,
    required this.participants,
  });

  factory TeacherClass.fromMap(Map<String, dynamic> m) => TeacherClass(
    scheduleId: (m['schedule_id'] as num?)?.toInt() ?? 0,
    serviceId: (m['service_id'] as num?)?.toInt() ?? 0,
    className: (m['class_name'] ?? '').toString(),
    colorClass: (m['color_class'])?.toString(),
    dateTime: (m['date_time'] ?? '').toString(),
    maxCapacity: (m['max_capacity'] as num?)?.toInt() ?? 0,
    bookedCount: (m['booked_count'] as num?)?.toInt() ?? 0,
    isFull: m['is_full'] == true,
    participants: ((m['participants'] as List?) ?? [])
        .cast<Map<String, dynamic>>()
        .map(TeacherParticipant.fromMap)
        .toList(),
  );
}

/// Utente trovato nella ricerca (per aggiungi partecipante).
class TeacherUser {
  final int id;
  final String firstName;
  final String lastName;
  final String email;

  const TeacherUser({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
  });

  String get fullName => '$firstName $lastName'.trim();

  factory TeacherUser.fromMap(Map<String, dynamic> m) => TeacherUser(
    id: (m['id'] as num?)?.toInt() ?? 0,
    firstName: (m['first_name'] ?? '').toString(),
    lastName: (m['last_name'] ?? '').toString(),
    email: (m['email'] ?? '').toString(),
  );
}

/// Ordine/pacchetto di un utente con residui.
class TeacherOrder {
  final int idOrderBook;
  final String serviceName;
  final int tickets;
  final int used;
  final int remaining;
  final String? expireOn;

  const TeacherOrder({
    required this.idOrderBook,
    required this.serviceName,
    required this.tickets,
    required this.used,
    required this.remaining,
    required this.expireOn,
  });

  factory TeacherOrder.fromMap(Map<String, dynamic> m) => TeacherOrder(
    idOrderBook: (m['idorderbook'] as num?)?.toInt() ?? 0,
    serviceName: (m['service_name'] ?? '').toString(),
    tickets: (m['tickets'] as num?)?.toInt() ?? 0,
    used: (m['used'] as num?)?.toInt() ?? 0,
    remaining: (m['remaining'] as num?)?.toInt() ?? 0,
    expireOn: (m['expireon'])?.toString(),
  );
}

class TeacherApi {
  static Map<String, String> _headers(String token) => {
    'Accept': 'application/json',
    'Authorization': 'Bearer $token',
  };

  /// Classi di un mese (YYYY-MM) con partecipanti. Solo staff lato server.
  static Future<List<TeacherClass>> fetchClasses({
    required String token,
    required String month,
  }) async {
    final query = month.isNotEmpty ? '?month=$month' : '';
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_classes.php$query',
    );

    final res = await http.get(uri, headers: _headers(token));

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(data['error'] ?? data['message'] ?? 'Errore classi');
    }

    return ((data['classes'] as List?) ?? [])
        .cast<Map<String, dynamic>>()
        .map(TeacherClass.fromMap)
        .toList();
  }

  /// Segna una prenotazione come persa (lost=true) o la ripristina (false).
  static Future<void> setLost({
    required String token,
    required int bookingId,
    required bool lost,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_mark_lost.php',
    );

    final res = await http.post(
      uri,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer $token',
      },
      body: 'booking_id=$bookingId&lost=${lost ? 'Y' : 'N'}',
    );

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(
        data['error'] ?? data['message'] ?? 'Errore aggiornamento',
      );
    }
  }

  /// Elimina una prenotazione (libera il ticket all'allievo).
  static Future<void> deleteBooking({
    required String token,
    required int bookingId,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_delete_booking.php',
    );

    final res = await http.post(
      uri,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer $token',
      },
      body: 'booking_id=$bookingId',
    );

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(
        data['error'] ?? data['message'] ?? 'Errore eliminazione',
      );
    }
  }

  /// Classi destinazione per riprogrammare una prenotazione
  /// (future, non piene, escluse quelle dove è già iscritto).
  static Future<List<TeacherClass>> availableClasses({
    required String token,
    required int bookingId,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_available_classes.php?booking_id=$bookingId',
    );

    final res = await http.get(
      uri,
      headers: {'Accept': 'application/json', 'Authorization': 'Bearer $token'},
    );

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(data['error'] ?? data['message'] ?? 'Errore classi');
    }

    return ((data['classes'] as List?) ?? [])
        .cast<Map<String, dynamic>>()
        .map(TeacherClass.fromMap)
        .toList();
  }

  /// Esegue la riprogrammazione su una nuova classe.
  static Future<void> reprogram({
    required String token,
    required int bookingId,
    required int scheduleId,
    required bool countAsReprogram,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_reprogram.php',
    );

    final res = await http.post(
      uri,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer $token',
      },
      body:
          'booking_id=$bookingId&schedule_id=$scheduleId'
          '&is_reprogrammed=${countAsReprogram ? 'Y' : 'N'}',
    );

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(
        data['error'] ?? data['message'] ?? 'Errore riprogrammazione',
      );
    }
  }

  /// Ricerca utenti per nome/cognome (min 2 caratteri in uno dei due).
  static Future<List<TeacherUser>> searchUsers({
    required String token,
    required String first,
    required String last,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_search_users.php'
      '?first=${Uri.encodeQueryComponent(first)}'
      '&last=${Uri.encodeQueryComponent(last)}',
    );

    final res = await http.get(uri, headers: _headers(token));

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(data['error'] ?? data['message'] ?? 'Errore ricerca');
    }

    return ((data['results'] as List?) ?? [])
        .cast<Map<String, dynamic>>()
        .map(TeacherUser.fromMap)
        .toList();
  }

  /// Ordini con residui di un utente.
  static Future<List<TeacherOrder>> userOrders({
    required String token,
    required int userId,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_user_orders.php?user_id=$userId',
    );

    final res = await http.get(uri, headers: _headers(token));

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(data['error'] ?? data['message'] ?? 'Errore ordini');
    }

    return ((data['orders'] as List?) ?? [])
        .cast<Map<String, dynamic>>()
        .map(TeacherOrder.fromMap)
        .toList();
  }

  /// Aggiunge un partecipante a una classe.
  /// [mode] = 'scala' | 'omaggio' | 'nuovo'.
  /// Per utente esistente passare [userId] > 0; per nuovo utente passare
  /// [userId] = 0 con name/surname (email opzionale).
  static Future<void> addBooking({
    required String token,
    required int scheduleId,
    required int serviceId,
    required String bookingStart,
    required String mode,
    int userId = 0,
    String name = '',
    String surname = '',
    String email = '',
    int? orderId,
    String? newExpiry,
  }) async {
    final uri = Uri.parse(
      '${ApiConfig.laravelApiBase}/api_teacher_add_booking.php',
    );

    final body = <String, String>{
      'idserviceschedule': '$scheduleId',
      'idservice': '$serviceId',
      'bookingstart': bookingStart,
      'bkmode': mode,
      'user_id': '$userId',
      'name': name,
      'surname': surname,
      'email': email,
      if (orderId != null) 'idorder': '$orderId',
      if (newExpiry != null) 'new_expiry': newExpiry,
    };

    final res = await http.post(
      uri,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer $token',
      },
      body: body.entries
          .map(
            (e) =>
                '${Uri.encodeQueryComponent(e.key)}='
                '${Uri.encodeQueryComponent(e.value)}',
          )
          .join('&'),
    );

    Map<String, dynamic> data;
    try {
      data = jsonDecode(res.body) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('RAW(${res.statusCode}): ${res.body}');
    }

    if (res.statusCode != 200 || data['success'] != true) {
      throw Exception(data['error'] ?? data['message'] ?? 'Errore inserimento');
    }
  }
}
