import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/features/attachments/data/attachments_repository.dart';
import 'package:flux/features/attachments/models/attachment_model.dart';
import 'package:get_it/get_it.dart';
import 'package:image_picker/image_picker.dart';

part 'attachments_events.dart';
part 'attachments_state.dart';

class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
  final _repository = GetIt.I.get<AttachmentsRepository>();

  AttachmentsBloc({String? parentId, required AttachmentParentType parentType})
    : super(
        AttachmentsState(
          status: AttachmentsStatus.initial,
          parentId: parentId,
          parentType: parentType,
        ),
      ) {
    on<ParentEntitySavedEvent>(_onParentEntitySaved);
    on<LoadAttachmentsEvent>(_onLoadAttachments);
    on<AddAttachmentsEvent>(_onAddAttachments);
    on<UploadAttachmentsEvent>(_onUploadAttachments);
    on<DeleteAttachmentsEvent>(_onDeleteAttachments);
    on<ToggleAttachmentSelectionEvent>(_onToggleAttachmentSelection);
    on<LinkAttachmentsToEntityEvent>(_onLinkAttachmentsToEntity);
    on<RenameAttachmentEvent>(_onRenameAttachment);
    on<DeleteSpecificAttachmentEvent>(_onDeleteSpecificAttachment);
    on<SelectAllAttachmentsEvent>(_onSelectAllAttachments);
    on<ClearAttachmentSelectionEvent>(_onClearAttachmentSelection);

    final currentCompanyId = GetIt.I.get<SessionCubit>().state.company?.id;
    if (parentId != null && currentCompanyId != null) {
      add(LoadAttachmentsEvent(parentId: parentId));
    }
  }

  FutureOr<void> _onParentEntitySaved(
    ParentEntitySavedEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    final companyId = GetIt.I.get<SessionCubit>().state.company?.id;

    emit(
      state.copyWith(
        parentId: event.newParentId,
        status: AttachmentsStatus.uploading,
      ),
    );

    if (state.localFiles.isNotEmpty) {
      try {
        final List<Future<void>> uploadTasks = state.localFiles.map((file) {
          final fakePlatformFile = PlatformFile(
            name: '${file.name}.${file.extension}',
            size: file.fileSize,
            bytes: file.localBytes,
          );

          // Chiamiamo il metodo generico passando il parentId e il TYPE
          return _repository.uploadAndRegisterFile(
            parentId: event.newParentId,
            parentType: state.parentType,
            pickedFile: fakePlatformFile,
            companyId: companyId!,
            bucket: _getBucketForParentType,
          );
        }).toList();

        await Future.wait(uploadTasks);
      } catch (e) {
        emit(
          state.copyWith(
            status: AttachmentsStatus.failure,
            error: "Errore upload post-salvataggio: $e",
          ),
        );
        return;
      }
    }

    emit(state.copyWith(localFiles: [], status: AttachmentsStatus.ready));
    add(LoadAttachmentsEvent(parentId: event.newParentId));
  }

  FutureOr<void> _onLoadAttachments(
    LoadAttachmentsEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    final currentId = event.parentId ?? state.parentId;

    if (currentId != null) {
      emit(state.copyWith(status: AttachmentsStatus.loading));

      await emit.forEach(
        _repository.getFilesStream(
          currentId,
          state.parentType,
        ), // Passiamo il tipo!
        onData: (List<AttachmentModel> data) =>
            state.copyWith(status: AttachmentsStatus.ready, remoteFiles: data),
        onError: (error, stackTrace) => state.copyWith(
          status: AttachmentsStatus.failure,
          error: error.toString(),
        ),
      );
    }
  }

  void _onAddAttachments(
    AddAttachmentsEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    final currentId = state.parentId;
    final currentCompanyId = GetIt.I.get<SessionCubit>().state.company?.id;

    if (currentCompanyId == null) {
      emit(
        state.copyWith(
          status: AttachmentsStatus.failure,
          error: "Company ID non trovato nella sessione",
        ),
      );
      return;
    }

    // BIVIO 1: PRATICA NUOVA (Salvataggio locale in memoria)
    if (currentId == null) {
      final newLocalFiles = event.files.map((file) {
        // FISCHIO SALVAVITA PER DESKTOP: se i bytes sono nulli, li leggiamo dal path fisico!
        Uint8List? rawBytes = file.bytes;
        if (rawBytes == null && file.path != null) {
          rawBytes = File(file.path!).readAsBytesSync();
        }

        return AttachmentModel(
          id: null,
          companyId: currentCompanyId,
          operationId: state.parentType == AttachmentParentType.operation
              ? ''
              : null,
          ticketId: state.parentType == AttachmentParentType.ticket ? '' : null,
          customerId: state.parentType == AttachmentParentType.customer
              ? ''
              : null,
          name: file.name.fileNameWithoutExtension(),
          extension: file.name.fileExtension(),
          storagePath: '',
          fileSize: file.size,
          localBytes: rawBytes, // Ora i byte ci sono al 100% anche su Mac!
        );
      }).toList();

      emit(
        state.copyWith(
          localFiles: [...state.localFiles, ...newLocalFiles],
          status: AttachmentsStatus.ready,
        ),
      );
      return;
    }

    // BIVIO 2: PRATICA ESISTENTE (Upload immediato)
    emit(state.copyWith(status: AttachmentsStatus.uploading));
    try {
      final List<Future<void>> uploadTasks = event.files.map((file) {
        return _repository.uploadAndRegisterFile(
          parentId: currentId,
          parentType: state.parentType,
          pickedFile: file,
          companyId: currentCompanyId,
          bucket: _getBucketForParentType,
        );
      }).toList();

      await Future.wait(uploadTasks);
      emit(state.copyWith(status: AttachmentsStatus.ready));
    } catch (e) {
      emit(
        state.copyWith(status: AttachmentsStatus.failure, error: e.toString()),
      );
    }
  }

  FutureOr<void> _onUploadAttachments(
    UploadAttachmentsEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    if ((event.pickedFiles == null || event.pickedFiles!.isEmpty) &&
        (event.photos == null || event.photos!.isEmpty)) {
      return;
    }

    if (state.parentId == null) return;

    emit(state.copyWith(status: AttachmentsStatus.uploading));
    try {
      final List<Future<void>> uploadTasks = [];

      // 1. Gestione Documenti normali (PlatformFile)
      if (event.pickedFiles != null) {
        for (var file in event.pickedFiles!) {
          uploadTasks.add(
            _repository.uploadAndRegisterFile(
              parentId: state.parentId!,
              parentType: state.parentType,
              pickedFile: file,
              companyId: event.companyId,
              bucket: _getBucketForParentType,
            ),
          );
        }
      }

      // 2. Gestione Foto Fotocamera (XFile)
      if (event.photos != null) {
        for (var photo in event.photos!) {
          // Leggiamo i byte asincronamente
          final bytes = await photo.readAsBytes();
          final fileSize = await photo.length();

          // Lo travestiamo da PlatformFile per passarlo al Repository!
          final fakePlatformFile = PlatformFile(
            name: photo.name,
            size: fileSize,
            bytes: bytes,
            path: photo.path,
          );

          uploadTasks.add(
            _repository.uploadAndRegisterFile(
              parentId: state.parentId!,
              parentType: state.parentType,
              pickedFile: fakePlatformFile,
              companyId: event.companyId,
              bucket: _getBucketForParentType,
            ),
          );
        }
      }

      // Esecuzione parallela di tutti i documenti e foto
      await Future.wait(uploadTasks);
      emit(state.copyWith(status: AttachmentsStatus.success));
    } catch (e) {
      emit(
        state.copyWith(status: AttachmentsStatus.failure, error: e.toString()),
      );
    }
  }

  FutureOr<void> _onDeleteAttachments(
    DeleteAttachmentsEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    emit(state.copyWith(status: AttachmentsStatus.loading));
    try {
      await _repository.deleteFiles(
        files: state.selectedFiles,
        currentContextType: state.parentType,
        bucket: _getBucketForParentType,
      );
      emit(state.copyWith(status: AttachmentsStatus.ready, selectedFiles: []));
    } catch (e) {
      emit(
        state.copyWith(status: AttachmentsStatus.failure, error: e.toString()),
      );
    }
  }

  FutureOr<void> _onToggleAttachmentSelection(
    ToggleAttachmentSelectionEvent event,
    Emitter<AttachmentsState> emit,
  ) {
    final selectedFiles = List<AttachmentModel>.from(state.selectedFiles);
    if (selectedFiles.contains(event.file)) {
      selectedFiles.remove(event.file);
    } else {
      selectedFiles.add(event.file);
    }
    emit(state.copyWith(selectedFiles: selectedFiles));
  }

  void _onSelectAllAttachments(
    SelectAllAttachmentsEvent event,
    Emitter<AttachmentsState> emit,
  ) {
    // Prendiamo TUTTI i file (locali e remoti) e li buttiamo nei selezionati
    emit(state.copyWith(selectedFiles: state.allFiles));
  }

  void _onClearAttachmentSelection(
    ClearAttachmentSelectionEvent event,
    Emitter<AttachmentsState> emit,
  ) {
    // Svuotiamo brutalmente la lista
    emit(state.copyWith(selectedFiles: []));
  }

  FutureOr<void> _onLinkAttachmentsToEntity(
    LinkAttachmentsToEntityEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    if (state.selectedFiles.isEmpty) return;

    // BIVIO 1: PRATICA/TICKET NON ANCORA SALVATA (Modalità Locale)
    if (state.parentId == null) {
      final updatedLocalFiles = state.localFiles.map((file) {
        if (state.selectedFiles.contains(file)) {
          // Assegniamo dinamicamente l'ID in base all'entità scelta
          switch (event.targetType) {
            case AttachmentParentType.customer:
              return file.copyWith(customerId: event.targetId);
            case AttachmentParentType.ticket:
              return file.copyWith(ticketId: event.targetId);
            case AttachmentParentType.operation:
              return file.copyWith(operationId: event.targetId);
            case AttachmentParentType.shippingDocument:
              return file.copyWith(shippingDocumentId: event.targetId);
            case AttachmentParentType.note:
              return file.copyWith(noteId: event.targetId);
          }
        }
        return file;
      }).toList();

      emit(
        state.copyWith(
          localFiles: updatedLocalFiles,
          selectedFiles: [], // Svuotiamo la selezione
          status: AttachmentsStatus.ready,
        ),
      );
      return;
    }

    // BIVIO 2: PRATICA/TICKET ESISTENTE (Modalità Remota su DB)
    emit(state.copyWith(status: AttachmentsStatus.loading));
    try {
      final List<Future<void>> linkTasks = [];

      for (var file in state.selectedFiles) {
        if (file.id != null) {
          linkTasks.add(
            _repository.linkFileToEntity(
              fileId: file.id!,
              targetType: event.targetType,
              targetId: event.targetId,
            ),
          );
        }
      }

      await Future.wait(linkTasks);

      // Lo stream aggiornerà automaticamente la UI
      emit(state.copyWith(status: AttachmentsStatus.ready, selectedFiles: []));
    } catch (e) {
      emit(
        state.copyWith(
          status: AttachmentsStatus.failure,
          error: "Errore durante il collegamento: $e",
        ),
      );
    }
  }

  FutureOr<void> _onRenameAttachment(
    RenameAttachmentEvent event,
    Emitter<AttachmentsState> emit,
  ) async {
    // BIVIO 1: File Locale (Bozza)
    if (event.file.localBytes != null) {
      final updatedLocalFiles = state.localFiles.map((f) {
        if (f == event.file) {
          return f.copyWith(name: event.newName);
        }
        return f;
      }).toList();
      emit(state.copyWith(localFiles: updatedLocalFiles));
      return;
    }

    // BIVIO 2: File Remoto (Salvato su DB)
    emit(state.copyWith(status: AttachmentsStatus.loading));
    try {
      await _repository.renameAttachment(event.file.id!, event.newName);
      emit(state.copyWith(status: AttachmentsStatus.ready));
    } catch (e) {
      emit(
        state.copyWith(
          status: AttachmentsStatus.failure,
          error: "Errore rinomina: $e",
        ),
      );
    }
  }

  FutureOr<void> _onDeleteSpecificAttachment(
    DeleteSpecificAttachmentEvent event,
    Emitter<AttachmentsState> emit,
  ) {
    if (event.file.localBytes != null) {
      final updatedLocalFiles = state.localFiles
          .where((f) => f != event.file)
          .toList();
      emit(state.copyWith(localFiles: updatedLocalFiles));
    }
  }

  Bucket get _getBucketForParentType {
    switch (state.parentType) {
      case AttachmentParentType.customer:
        return Bucket.documents;
      case AttachmentParentType.ticket:
        return Bucket.documents;
      case AttachmentParentType.operation:
        return Bucket.documents;
      case AttachmentParentType.shippingDocument:
        return Bucket.companyDocuments;
      case AttachmentParentType.note:
        return Bucket.documents;
    }
  }
}
