import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:social_app/features/chat/data/models/ag_ui_event.dart';
import 'package:social_app/features/chat/data/models/chat_list_item.dart';
import 'package:social_app/features/chat/data/services/ag_ui_service.dart';
import 'package:social_app/features/chat/presentation/bloc/chat_bloc.dart';

class MockAgUiService extends AgUiService {
  MockAgUiService() : super(onEvent: (_) {});

  @override
  Future<void> sendMessage(String content) async {}
}

void main() {
  late ChatBloc chatBloc;
  late AgUiService service;

  setUp(() {
    service = MockAgUiService();
    chatBloc = ChatBloc(service: service);
  });

  tearDown(() {
    chatBloc.close();
  });

  group('ChatBloc', () {
    test('initial state is empty', () {
      expect(chatBloc.state.items, isEmpty);
      expect(chatBloc.state.isLoading, false);
      expect(chatBloc.state.currentMessageId, isNull);
      expect(chatBloc.state.error, isNull);
    });

    blocTest<ChatBloc, ChatState>(
      'sendMessage adds user message to items',
      build: () => chatBloc,
      act: (bloc) => bloc.sendMessage('Hello'),
      expect: () => [
        isA<ChatState>()
            .having((state) => state.items.length, 'items length', 1)
            .having(
              (state) => state.items.first,
              'first item',
              isA<TextMessageItem>().having(
                (item) => item.content,
                'content',
                'Hello',
              ),
            ),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'textMessageStart event adds AI message with streaming',
      build: () => chatBloc,
      act: (bloc) {
        bloc.emit(chatBloc.state.copyWith(isLoading: true));
        service.onEvent(
          TextMessageStartEvent(messageId: 'msg_1', role: 'assistant'),
        );
      },
      expect: () => [
        isA<ChatState>()
            .having((s) => s.isLoading, 'isLoading', true)
            .having((s) => s.isLoading, 'isLoading', true),
        isA<ChatState>()
            .having((s) => s.items.length, 'items length', 1)
            .having((s) => s.currentMessageId, 'currentMessageId', 'msg_1')
            .having(
              (s) => s.items.first,
              'first item',
              isA<TextMessageItem>()
                  .having((item) => item.isStreaming, 'isStreaming', true)
                  .having((item) => item.sender, 'sender', MessageSender.ai),
            ),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'textMessageContent event appends content',
      build: () => chatBloc,
      seed: () => ChatState(
        items: [
          TextMessageItem(
            id: 'msg_1',
            content: '',
            timestamp: DateTime.now(),
            sender: MessageSender.ai,
            isStreaming: true,
          ),
        ],
        currentMessageId: 'msg_1',
      ),
      act: (bloc) {
        service.onEvent(
          TextMessageContentEvent(messageId: 'msg_1', delta: 'Hello'),
        );
      },
      expect: () => [
        isA<ChatState>().having(
          (s) => (s.items.first as TextMessageItem).content,
          'content',
          'Hello',
        ),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'textMessageEnd event sets isStreaming to false',
      build: () => chatBloc,
      seed: () => ChatState(
        items: [
          TextMessageItem(
            id: 'msg_1',
            content: 'Hello World',
            timestamp: DateTime.now(),
            sender: MessageSender.ai,
            isStreaming: true,
          ),
        ],
        currentMessageId: 'msg_1',
      ),
      act: (bloc) {
        service.onEvent(TextMessageEndEvent(messageId: 'msg_1'));
      },
      expect: () => [
        isA<ChatState>()
            .having((s) => s.currentMessageId, 'currentMessageId', isNull)
            .having(
              (s) => (s.items.first as TextMessageItem).isStreaming,
              'isStreaming',
              false,
            ),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'runStarted sets isLoading to true',
      build: () => chatBloc,
      act: (bloc) {
        service.onEvent(RunStartedEvent(threadId: 't1', runId: 'r1'));
      },
      expect: () => [
        isA<ChatState>()
            .having((s) => s.isLoading, 'isLoading', true)
            .having((s) => s.error, 'error', isNull),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'runFinished sets isLoading to false',
      build: () => chatBloc,
      seed: () => const ChatState(isLoading: true),
      act: (bloc) {
        service.onEvent(RunFinishedEvent(threadId: 't1', runId: 'r1'));
      },
      expect: () => [
        isA<ChatState>()
            .having((s) => s.isLoading, 'isLoading', false)
            .having((s) => s.currentMessageId, 'currentMessageId', isNull),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'runError sets error message',
      build: () => chatBloc,
      seed: () => const ChatState(isLoading: true),
      act: (bloc) {
        service.onEvent(
          RunErrorEvent(message: 'Something went wrong', code: 'ERR'),
        );
      },
      expect: () => [
        isA<ChatState>()
            .having((s) => s.isLoading, 'isLoading', false)
            .having((s) => s.error, 'error', 'Something went wrong'),
      ],
    );

    blocTest<ChatBloc, ChatState>(
      'clearError removes error',
      build: () => chatBloc,
      seed: () => const ChatState(error: 'Some error'),
      act: (bloc) => bloc.clearError(),
      expect: () => [isA<ChatState>().having((s) => s.error, 'error', isNull)],
    );

    blocTest<ChatBloc, ChatState>(
      'toolCallStart adds ToolCallItem',
      build: () => chatBloc,
      act: (bloc) {
        service.onEvent(
          ToolCallStartEvent(
            toolCallId: 'tc_1',
            toolCallName: 'create_calendar_event',
          ),
        );
      },
      expect: () => [
        isA<ChatState>().having(
          (s) {
            final item = s.items.first;
            return item is ToolCallItem &&
                item.toolName == 'create_calendar_event' &&
                item.status == ToolCallStatus.pending;
          },
          'has pending tool call',
          true,
        ),
      ],
    );
  });
}
