import 'package:file_picker/file_picker.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/enums_and_consts/consts.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/features/attachments/models/attachment_model.dart';
import 'package:get_it/get_it.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '../models/customer_model.dart';

class CustomerRepository {
  final SupabaseClient _supabase = GetIt.I<SupabaseClient>();
  final String companyId = GetIt.I.get<SessionCubit>().state.company!.id!;

  // Crea un nuovo cliente
  Future<CustomerModel> insertCustomer(CustomerModel customer) async {
    try {
      final response = await _supabase
          .from(Tables.customers)
          .upsert(customer.toJson())
          .select()
          .single();
      return CustomerModel.fromMap(response);
    } catch (e) {
      throw '$e';
    }
  }

  Future<CustomerModel> updateCustomer(CustomerModel customer) async {
    try {
      final response = await _supabase
          .from(Tables.customers)
          .update(customer.toJson())
          .eq('id', customer.id!)
          .select()
          .single();
      return CustomerModel.fromMap(response);
    } catch (e) {
      throw '$e';
    }
  }

  // Recupera tutti i clienti dell'azienda
  Future<List<CustomerModel>> getCustomers(String companyId) async {
    try {
      final response = await _supabase
          .from(Tables.customers)
          .select('''
            *,
            ${Tables.attachments}(*)
            ''')
          .eq('company_id', companyId)
          .eq('is_active', true)
          .order('name', ascending: true);

      return (response as List).map((c) => CustomerModel.fromMap(c)).toList();
    } catch (e) {
      throw '$e';
    }
  }

  Future<CustomerModel> getCustomerById(String customerId) async {
    try {
      final response = await _supabase
          .from(Tables.customers)
          .select('''
            *,
            ${Tables.attachments}(*)
            ''')
          .eq('id', customerId)
          .single();

      return CustomerModel.fromMap(response);
    } catch (e) {
      throw '$e';
    }
  }

  // Ricerca clienti per nome o telefono (fondamentale per la UX)
  Future<List<CustomerModel>> searchCustomers(
    String companyId,
    String query,
  ) async {
    try {
      final response = await _supabase
          .from(Tables.customers)
          .select()
          .eq('company_id', companyId)
          .or('name.ilike.%$query%,phone_number.ilike.%$query%')
          .limit(10);

      return (response as List).map((c) => CustomerModel.fromMap(c)).toList();
    } catch (e) {
      return [];
    }
  }

  /// Ascolta in tempo reale i file caricati per un cliente
  Stream<List<AttachmentModel>> getCustomerFilesStream(String customerId) {
    return _supabase
        .from(Tables.attachments)
        .stream(primaryKey: ['id'])
        .eq('customer_id', customerId)
        .order('created_at', ascending: false)
        .map(
          (listOfMaps) =>
              listOfMaps.map((map) => AttachmentModel.fromMap(map)).toList(),
        );
  }

  /// Recupera i file di un cliente specifico
  Future<List<AttachmentModel>> getCustomerFiles(String customerId) async {
    try {
      final response = await _supabase
          .from(Tables.attachments)
          .select()
          .eq('customer_id', customerId);

      return (response as List).map((f) => AttachmentModel.fromMap(f)).toList();
    } catch (e) {
      throw '$e';
    }
  }

  /// Carica un file e salva il riferimento nel database
  Future<AttachmentModel> uploadAndRegisterFile({
    required String customerId,
    required PlatformFile pickedFile,
  }) async {
    final cleanFileName = pickedFile.name.replaceAll(
      RegExp(r'[^a-zA-Z0-9\.\-]'),
      '_',
    );
    final storagePath =
        '$companyId/customers/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
    final int fileSize = pickedFile.size;
    final fileToSave = AttachmentModel(
      companyId: companyId,
      customerId: customerId,
      name: cleanFileName.fileNameWithoutExtension(),
      extension: cleanFileName.fileExtension(),
      storagePath: storagePath,
      fileSize: fileSize,
    );
    final String mimeType = fileToSave.extension.toLowerCase() == 'pdf'
        ? 'application/pdf'
        : 'image/${fileToSave.extension}';
    try {
      // Usiamo bytes invece del path per massima compatibilità
      if (pickedFile.bytes == null && pickedFile.path == null) {
        throw 'File read error';
      }

      // Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes
      if (pickedFile.bytes != null) {
        await _supabase.storage
            .from('documents')
            .uploadBinary(
              storagePath,
              pickedFile.bytes!,
              fileOptions: FileOptions(contentType: mimeType, upsert: true),
            );
      }

      final response = await _supabase
          .from(Tables.attachments)
          .insert(fileToSave.toMap())
          .select()
          .single();

      return AttachmentModel.fromMap(response);
    } catch (e) {
      throw '$e';
    }
  }

  Future<void> saveFileReference(AttachmentModel file) async {
    await _supabase.from(Tables.attachments).upsert(file.toMap());
  }

  Future<void> deleteDocuments(List<AttachmentModel> files) async {
    if (files.isEmpty) return;
    // 1. Prepariamo le liste di ID e di Percorsi
    final List<String> idsToDelete = [];
    final List<String> storagePathsToDelete = [];
    final List<String> idsToEdit = [];
    for (var file in files) {
      if (file.operationId == null) {
        idsToDelete.add(file.id!);
        storagePathsToDelete.add(file.storagePath!);
      } else {
        idsToEdit.add(file.id!);
      }
    }
    try {
      if (idsToDelete.isNotEmpty) {
        await _supabase
            .from(Tables.attachments)
            .delete()
            .inFilter('id', idsToDelete);
        // 3. Cancellazione MASSIVA dallo Storage
        await _supabase.storage.from('documents').remove(storagePathsToDelete);
      }
      if (idsToEdit.isNotEmpty) {
        await _supabase
            .from(Tables.attachments)
            .update({'customer_id': null})
            .inFilter('id', idsToEdit);
      }
    } on PostgrestException catch (e) {
      throw e.message;
    } catch (e) {
      throw '$e';
    }
  }
}
