import 'package:flutter/foundation.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/enums_and_consts/consts.dart';
import 'package:flux/features/notes/models/note_model.dart';
import 'package:get_it/get_it.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class NotesRepository {
  final _supabase = GetIt.I.get<SupabaseClient>();

  String get _companyId => GetIt.I.get<SessionCubit>().state.company!.id!;
  String get _currentStaffId =>
      GetIt.I.get<SessionCubit>().state.currentStaffMember!.id!;

  /// Recupera tutte le note visibili dall'utente corrente:
  /// 1. Note create da lui
  /// 2. Note condivise a tutti (is_shared_all = true)
  /// 3. Note in cui l'utente è esplicitamente un collaboratore
  Future<List<NoteModel>> getNotes() async {
    try {
      // Usiamo la sintassi avanzata di Supabase per fare il filtro OR sulle relazioni
      // Inoltre tiriamo giù note_collaborators e l'anagrafica dei membri dello staff associati
      final response = await _supabase
          .from(Tables.notes)
          .select('''
            *,
            ${Tables.noteCollaborators}!inner (
              staff_id,
              ${Tables.staffMembers} (*)
            )
          ''')
          // Filtro multi-tenant di sicurezza (già ridondante con RLS ma ottimo per performance)
          .eq('company_id', _companyId)
          // Questa è la magia: l'utente vede la nota se è sua, se è pubblica o se è tra i collaboratori
          .or(
            'created_by.eq.$_currentStaffId,is_shared_all.eq.true,note_collaborators.staff_id.eq.$_currentStaffId',
          )
          // Ordiniamo prima per sticky (pinned) e poi per data di aggiornamento
          .order('is_pinned', ascending: false)
          .order('updated_at', ascending: false);

      return (response as List)
          .map((json) => NoteModel.fromMap(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      // In caso di errore sulla join !inner se non ci sono collaboratori,
      // facciamo un fallback pulito su una query standard e uniamo i dati.
      return _getNotesFallback();
    }
  }

  /// Fallback sicuro nel caso la query complessa con !inner si inceppi se la lista collaboratori è vuota
  Future<List<NoteModel>> _getNotesFallback() async {
    final response = await _supabase
        .from(Tables.notes)
        .select('''
          *,
          ${Tables.noteCollaborators} (
            staff_id,
            ${Tables.staffMembers} (*)
          )
        ''')
        .eq('company_id', _companyId)
        .order('is_pinned', ascending: false)
        .order('updated_at', ascending: false);

    final allNotes = (response as List)
        .map((json) => NoteModel.fromMap(json as Map<String, dynamic>))
        .toList();

    // Filtriamo lato codice per essere sicuri della visibilità
    return allNotes.where((note) {
      return note.createdBy == _currentStaffId ||
          note.isSharedAll ||
          note.collaboratorIds.contains(_currentStaffId);
    }).toList();
  }

  Stream<List<NoteModel>> notesStream({
    required String companyId,
    required String currentStaffId,
  }) {
    return _supabase
        .from(Tables.notes)
        .stream(primaryKey: ['id'])
        .eq('company_id', companyId)
        .order('is_pinned', ascending: false)
        // Nota: puoi ordinare solo per un campo alla volta nello stream nativo di Supabase.
        // Ordiniamo per is_pinned, l'ordinamento per data lo facciamo al volo in RAM se serve,
        // oppure ordiniamo per updated_at e il pin lo gestiamo via software.
        .map((rawNotes) {
          return rawNotes.map((json) => NoteModel.fromMap(json)).where((note) {
            // Filtro multi-tenant di sicurezza in memoria
            return note.createdBy == currentStaffId ||
                note.isSharedAll ||
                note.collaboratorIds.contains(currentStaffId);
          }).toList();
        });
  }

  Future<String> saveNote(NoteModel note) async {
    // 1. Eseguiamo l'upsert sulla tabella principale 'notes'
    // Supabase gestisce in automatico: se l'id è null inserisce, se c'è fa update.
    // Usiamo il .select().single() per farci restituire l'id generato (in caso di nuova nota)
    final noteResponse = await _supabase
        .from(Tables.notes)
        .upsert({
          if (note.id != null) 'id': note.id,
          'company_id': note.companyId,
          'created_by': note.createdBy,
          'title': note.title,
          'content': note.content,
          'color': note.color,
          'is_pinned': note.isPinned,
          'is_shared_all': note.isSharedAll,
          'updated_at': DateTime.now().toIso8601String(),
        })
        .select('id')
        .single();

    final noteId = noteResponse['id'] as String;

    // Se la nota è condivisa con tutti, spazziamo via eventuali collaboratori singoli e usciamo
    if (note.isSharedAll) {
      await _supabase.from('note_collaborators').delete().eq('note_id', noteId);
      return noteId;
    }

    // 2. LA STRATEGIA DEL PIAZZA PULITA SUI COLLABORATORI
    // Prima di tutto eliminiamo TUTTI i collaboratori attuali per questa specifica nota
    await _supabase.from('note_collaborators').delete().eq('note_id', noteId);

    // 3. RE-INSERIMENTO DELLA LISTA AGGIORNATA
    if (note.collaboratorIds.isNotEmpty) {
      final collaboratorsToInsert = note.collaboratorIds
          .map(
            (staffId) => {
              'note_id': noteId,
              'staff_id': staffId,
              'company_id': note.companyId, // Aggiunto questo!
            },
          )
          .toList();

      // Consiglio da pro: avvolgi l'insert in un try-catch per stampare l'errore esatto a console
      try {
        await _supabase
            .from(Tables.noteCollaborators)
            .insert(collaboratorsToInsert);
      } catch (e) {
        debugPrint('Errore inserimento collaboratori: $e');
        throw Exception('Impossibile aggiungere i collaboratori alla nota.');
      }
    }

    // Restituiamo l'id alla UI (fondamentale per la nostra logica Ninja di creazione)
    return noteId;
  }

  /// Elimina una nota (i collaboratori si cancellano in cascata grazie al vincolo del DB)
  Future<void> deleteNote(String noteId) async {
    await _supabase.from(Tables.notes).delete().eq('id', noteId);
  }
}
