import 'dart:async';

import 'package:em2rp/models/event_model.dart';
import 'package:em2rp/services/api_service.dart';
import 'package:em2rp/utils/debug_log.dart';

/// Représente un tour de conversation dans le chat.
class AiAssistantChatTurn {
  final bool isUser;
  final String text;

  const AiAssistantChatTurn({required this.isUser, required this.text});
}

/// Document à attacher pour demander à l'IA d'analyser un devis, etc.
class AiEquipmentDocument {
  final String base64Data;
  final String mimeType;
  final String? fileName;

  const AiEquipmentDocument({
    required this.base64Data,
    required this.mimeType,
    this.fileName,
  });
}

/// Un item proposé par l'IA dans la liste de matériel.
class AiEquipmentProposalItem {
  final String equipmentId;
  final int quantity;
  final String rationale;

  const AiEquipmentProposalItem({
    required this.equipmentId,
    required this.quantity,
    required this.rationale,
  });
}

/// Métadonnées pour un container proposé par l'IA.
class AiEquipmentProposalContainer {
  final String containerId;
  final String rationale;
  final List<String> equipmentIds;
  final List<String> matchingEquipmentIds;
  final List<String> missingEquipmentIds;
  final bool partial;
  final bool? available;
  final dynamic availabilityDetail;

  const AiEquipmentProposalContainer({
    required this.containerId,
    required this.rationale,
    this.equipmentIds = const [],
    this.matchingEquipmentIds = const [],
    this.missingEquipmentIds = const [],
    this.partial = false,
    this.available,
    this.availabilityDetail,
  });
}

/// Proposition complète retournée par l'IA.
class AiEquipmentProposal {
  final String summary;
  final List<AiEquipmentProposalItem> items;

  /// Équipements individuels prêts à être injectés dans l'état local de l'événement.
  final List<EventEquipment> asEventEquipment;

  /// Containers (métadonnées) proposés par l'IA.
  final List<AiEquipmentProposalContainer> containers;

  List<String> get containerIds => containers.map((c) => c.containerId).toList();

  const AiEquipmentProposal({
    required this.summary,
    required this.items,
    required this.asEventEquipment,
    required this.containers,
  });
}

/// Réponse complète de l'assistant IA (message + proposition optionnelle).
class AiEquipmentAssistantResponse {
  final String assistantMessage;
  final AiEquipmentProposal? proposal;
  final List<String> debugLogs;

  const AiEquipmentAssistantResponse({
    required this.assistantMessage,
    this.proposal,
    this.debugLogs = const [],
  });
}

/// Service assistant IA logisticien.
/// Délègue tous les appels Gemini à la Cloud Function [aiEquipmentProposal].
/// L'authentification Firebase (token Bearer) suffit — aucune clé API côté client.
class AiEquipmentAssistantService {
  final ApiService _apiService;

  AiEquipmentAssistantService({ApiService? apiService})
      : _apiService = apiService ?? FirebaseFunctionsApiService();

  /// Envoie un message et retourne la réponse de l'assistant IA.
  Future<AiEquipmentAssistantResponse> generateProposal({
    required DateTime startDate,
    required DateTime endDate,
    required List<AiAssistantChatTurn> history,
    required String userMessage,
    String? eventTypeId,
    String? excludeEventId,
    List<EventEquipment> currentAssignedEquipment = const [],
    List<EventEquipment> workingProposalEquipment = const [],
    AiEquipmentDocument? document,
  }) async {
    final payload = <String, dynamic>{
      'startDate': startDate.toIso8601String(),
      'endDate': endDate.toIso8601String(),
      'userMessage': userMessage.trim(),
      'history': history
          .where((turn) => turn.text.trim().isNotEmpty)
          .map((turn) => {'isUser': turn.isUser, 'text': turn.text.trim()})
          .toList(),
      'currentEquipment': currentAssignedEquipment
          .map((eq) => {'equipmentId': eq.equipmentId, 'quantity': eq.quantity})
          .toList(),
      'workingProposal': workingProposalEquipment
          .map((eq) => {'equipmentId': eq.equipmentId, 'quantity': eq.quantity})
          .toList(),
    };

    if (eventTypeId != null) payload['eventTypeId'] = eventTypeId;
    if (excludeEventId != null) payload['excludeEventId'] = excludeEventId;

    if (document != null) {
      payload['document'] = {
        'mimeType': document.mimeType,
        'data': document.base64Data,
        if (document.fileName != null) 'fileName': document.fileName,
      };
    }

    try {
      DebugLog.info('[AiEquipmentAssistantService] Calling aiEquipmentProposal Cloud Function');

      final result = await _apiService.call('aiEquipmentProposal', payload);
      final assistantMessage = result['assistantMessage']?.toString().trim() ?? '';
      final proposal = _parseProposal(result['proposal']);
      
      final rawLogs = result['debugLogs'];
      final debugLogs = (rawLogs is List) ? rawLogs.map((e) => e.toString()).toList() : <String>[];

      DebugLog.info(
        '[AiEquipmentAssistantService] Response received, items: ${proposal?.items.length ?? 0}',
      );

      return AiEquipmentAssistantResponse(
        assistantMessage: assistantMessage.isNotEmpty
            ? assistantMessage
            : 'Je n\'ai pas pu générer de réponse.',
        proposal: proposal,
        debugLogs: debugLogs,
      );
    } on ApiException catch (e) {
      DebugLog.error('[AiEquipmentAssistantService] API error', e);
      if (e.isUnauthorized) {
        throw Exception('Vous n\'êtes pas authentifié. Reconnectez-vous et réessayez.');
      }
      throw Exception('Erreur du service IA (${e.statusCode}): ${e.message}');
    } catch (e) {
      DebugLog.error('[AiEquipmentAssistantService] Error', e);
      rethrow;
    }
  }

  AiEquipmentProposal? _parseProposal(dynamic rawProposal) {
    if (rawProposal == null || rawProposal is! Map<String, dynamic>) return null;

    final proposalItems = <AiEquipmentProposalItem>[];
    final eventEquipmentList = <EventEquipment>[];
    // legacy containerIds variable removed (we now use containersMeta)

    final rawItems = rawProposal['items'];
    if (rawItems is List) {
      for (final rawItem in rawItems) {
        if (rawItem is! Map) continue;
        final item = Map<String, dynamic>.from(rawItem);

        final equipmentId = item['equipmentId']?.toString().trim() ?? '';
        final quantity = int.tryParse(item['quantity']?.toString() ?? '1') ?? 1;

        if (equipmentId.isEmpty || quantity <= 0) continue;

        final rationale = item['rationale']?.toString().trim() ?? 'Proposition IA';

        proposalItems.add(AiEquipmentProposalItem(
          equipmentId: equipmentId,
          quantity: quantity,
          rationale: rationale,
        ));
        eventEquipmentList.add(EventEquipment(
          equipmentId: equipmentId,
          quantity: quantity,
          rationale: rationale,
        ));
      }
    }

    final containersMeta = <AiEquipmentProposalContainer>[];
    final rawContainers = rawProposal['containers'];
    if (rawContainers is List) {
      for (final rawContainer in rawContainers) {
        if (rawContainer is String) {
          final cid = rawContainer.toString().trim();
          if (cid.isNotEmpty) {
            containersMeta.add(AiEquipmentProposalContainer(containerId: cid, rationale: 'Proposition IA'));
          }
          continue;
        }
        if (rawContainer is! Map) continue;
        final container = Map<String, dynamic>.from(rawContainer);
        final containerId = container['containerId']?.toString().trim() ?? '';
        if (containerId.isEmpty) continue;

        final rationale = container['rationale']?.toString().trim() ?? 'Proposition IA';
        final equipmentIds = <String>[];
        final matching = <String>[];
        final missing = <String>[];

        if (container['equipmentIds'] is List) {
          for (final v in container['equipmentIds']) {
            final s = v == null ? null : v.toString().trim();
            if (s != null && s.isNotEmpty) equipmentIds.add(s);
          }
        }
        if (container['matchingEquipmentIds'] is List) {
          for (final v in container['matchingEquipmentIds']) {
            final s = v == null ? null : v.toString().trim();
            if (s != null && s.isNotEmpty) matching.add(s);
          }
        }
        if (container['missingEquipmentIds'] is List) {
          for (final v in container['missingEquipmentIds']) {
            final s = v == null ? null : v.toString().trim();
            if (s != null && s.isNotEmpty) missing.add(s);
          }
        }

        final partial = container['partial'] is bool ? container['partial'] as bool : (missing.isNotEmpty);
        final available = container.containsKey('available') ? (container['available'] is bool ? container['available'] as bool : null) : null;
        final availabilityDetail = container.containsKey('availabilityDetail') ? container['availabilityDetail'] : null;

        containersMeta.add(AiEquipmentProposalContainer(
          containerId: containerId,
          rationale: rationale,
          equipmentIds: equipmentIds,
          matchingEquipmentIds: matching,
          missingEquipmentIds: missing,
          partial: partial,
          available: available,
          availabilityDetail: availabilityDetail,
        ));
      }
    }

    if (proposalItems.isEmpty && containersMeta.isEmpty) return null;

    return AiEquipmentProposal(
      summary: rawProposal['summary']?.toString().trim().isNotEmpty == true
          ? rawProposal['summary'].toString().trim()
          : 'Proposition matériel générée automatiquement.',
      items: proposalItems,
      asEventEquipment: eventEquipmentList,
      containers: containersMeta,
    );
  }
}
