import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flux/core/enums_and_consts/consts.dart';
import 'package:flux/features/attachments/models/attachment_model.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';

enum Bucket {
  documents('documents'),
  companyDocuments('company_documents');

  final String value;
  const Bucket(this.value);
}

class AttachmentsRepository {
  final _supabase = Supabase.instance.client;

  /// Scarica i byte di un file direttamente da Supabase Storage
  Future<Uint8List> downloadAttachmentBytes({
    required String storagePath,
    required Bucket bucket,
  }) async {
    try {
      final Uint8List bytes = await _supabase.storage
          .from(bucket.value)
          .download(storagePath);
      return bytes;
    } catch (e) {
      throw Exception("Impossibile scaricare il documento dal cloud: $e");
    }
  }

  /// RESTITUISCE IL NOME DELLA COLONNA DB IN BASE AL TIPO
  String _getColumnNameForParent(AttachmentParentType parentType) {
    switch (parentType) {
      case AttachmentParentType.operation:
        return 'operation_id';
      case AttachmentParentType.ticket:
        return 'ticket_id';
      case AttachmentParentType.customer:
        return 'customer_id';
      case AttachmentParentType.shippingDocument:
        return 'shipping_document_id';
      case AttachmentParentType.note:
        return 'note_id';
    }
  }

  /// RECUPERA I FILE IN TEMPO REALE
  Stream<List<AttachmentModel>> getFilesStream(
    String parentId,
    AttachmentParentType parentType,
  ) {
    final columnName = _getColumnNameForParent(parentType);

    return _supabase
        .from(Tables.attachments)
        .stream(primaryKey: ['id'])
        .eq(columnName, parentId)
        .map(
          (listOfMaps) =>
              listOfMaps.map((map) => AttachmentModel.fromMap(map)).toList(),
        );
  }

  /// CARICA IL FILE NELLO STORAGE E LO REGISTRA NEL DB
  Future<void> uploadAndRegisterFile({
    required String parentId,
    required AttachmentParentType parentType,
    required String companyId,
    required Bucket bucket,
    PlatformFile? pickedFile, // Ora è opzionale
    Uint8List? rawBytes, // Alternativa: bytes grezzi
    String? rawFileName, // Alternativa: nome del file
  }) async {
    // 🛡️ L'ASSERT NINJA: O c'è il pickedFile, o ci sono i byte e il nome.
    // L'assert funziona solo in debug, ma è perfetto per beccare subito errori di chiamata!
    assert(
      pickedFile != null || (rawBytes != null && rawFileName != null),
      'Devi passare o un PlatformFile, oppure rawBytes e rawFileName!',
    );

    try {
      // 1. Normalizziamo i dati in base a cosa ci è stato passato
      final Uint8List finalBytes;
      final String finalFileName;
      final int finalFileSize;

      if (pickedFile != null) {
        if (pickedFile.bytes == null) {
          throw Exception(
            "I bytes del file sono vuoti! Ricarica la pagina senza cache.",
          );
        }
        finalBytes = pickedFile.bytes!;
        finalFileName = pickedFile.name;
        finalFileSize = pickedFile.size;
      } else {
        // Se pickedFile è null, grazie all'assert sappiamo che questi non lo sono
        finalBytes = rawBytes!;
        finalFileName = rawFileName!;
        finalFileSize = finalBytes.length; // Calcoliamo la size dai byte reali
      }

      // 2. Estraiamo l'estensione e puliamo il nome
      final extension = finalFileName.contains('.')
          ? finalFileName.split('.').last
          : ''; // Fallback se il file non ha estensione

      final cleanName = finalFileName
          .replaceAll(RegExp(r'[^\w\s\.-]'), '')
          .replaceAll(' ', '_');

      // 3. Creiamo un path ordinato: idAzienda/tipoEntita/idEntita/timestamp_nomefile
      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final storagePath =
          '$companyId/${parentType.name}/$parentId/${timestamp}_$cleanName';

      // 4. Upload su Supabase Storage
      await _supabase.storage
          .from(bucket.value)
          .uploadBinary(
            storagePath,
            finalBytes,
            fileOptions: FileOptions(contentType: _guessContentType(extension)),
          );

      // 5. Creiamo la mappa per il DB dinamicamente
      final Map<String, dynamic> insertData = {
        'company_id': companyId,
        'name': finalFileName.replaceAll('.$extension', ''),
        'extension': extension,
        'file_size': finalFileSize,
        'storage_path': storagePath,
      };

      // Inseriamo l'ID nella colonna giusta!
      final columnName = _getColumnNameForParent(parentType);
      insertData[columnName] = parentId;

      // 6. Salviamo su Postgres
      await _supabase.from(Tables.attachments).insert(insertData);
    } catch (e) {
      throw Exception("Errore caricamento: $e");
    }
  }

  /// ELIMINA IL FILE (Scollegamento intelligente)
  Future<void> deleteFiles({
    required List<AttachmentModel> files,
    required AttachmentParentType currentContextType,
    required Bucket bucket,
  }) async {
    if (files.isEmpty) return;

    try {
      for (var file in files) {
        if (file.id == null) continue;

        // 1. Capiamo quali collegamenti ha questo file attualmente
        final currentLinks = {
          AttachmentParentType.operation: file.operationId,
          AttachmentParentType.ticket: file.ticketId,
          AttachmentParentType.customer: file.customerId,
          AttachmentParentType.shippingDocument: file.shippingDocumentId,
        };

        // 2. Simuliamo la rimozione del collegamento per il contesto attuale
        currentLinks[currentContextType] = null;

        // 3. Controlliamo se rimangono altri ID valorizzati
        final hasOtherActiveLinks = currentLinks.values.any(
          (id) => id != null && id.isNotEmpty,
        );

        if (hasOtherActiveLinks) {
          // A. Ci sono ancora altre entità che usano questo file!
          // Scolleghiamolo SOLO dal contesto attuale mettendo a NULL la sua colonna
          await _supabase
              .from(Tables.attachments)
              .update({currentContextType.dbColumn: null})
              .eq('id', file.id!);
        } else {
          // B. Nessuno usa più questo file! ELIMINAZIONE FISICA TOTALE.
          await _supabase.from(Tables.attachments).delete().eq('id', file.id!);

          if (file.storagePath != null) {
            await _supabase.storage.from(bucket.value).remove([
              file.storagePath!,
            ]);
          }
        }
      }
    } catch (e) {
      throw Exception("Errore nell'eliminazione dei file: $e");
    }
  }

  /// RINOMINA UN FILE (Solo nel DB, non cambiamo il file fisico)
  Future<void> renameAttachment(String fileId, String newName) async {
    try {
      await _supabase
          .from(Tables.attachments)
          .update({'name': newName})
          .eq('id', fileId);
    } catch (e) {
      throw Exception("Errore nella rinomina del file: $e");
    }
  }

  /// ASSOCIA UN FILE A UN'ALTRA ENTITÀ (Modifica il record esistente)
  Future<void> linkFileToEntity({
    required String fileId,
    required AttachmentParentType targetType,
    required String targetId,
  }) async {
    try {
      // Facciamo un semplice UPDATE aggiungendo l'ID nella colonna giusta
      await _supabase
          .from(Tables.attachments)
          .update({targetType.dbColumn: targetId})
          .eq('id', fileId);
    } catch (e) {
      throw Exception("Errore nel collegamento del file: $e");
    }
  }

  // Helper per indovinare il content-type base
  String _guessContentType(String extension) {
    switch (extension.toLowerCase()) {
      case 'pdf':
        return 'application/pdf';
      case 'png':
        return 'image/png';
      case 'jpg':
      case 'jpeg':
        return 'image/jpeg';
      default:
        return 'application/octet-stream';
    }
  }
}
