/// Conversation management endpoints for CQRS API
library;

import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:http/http.dart' as http;

import '../client.dart';
import '../types.dart';

// =============================================================================
// Commands
// =============================================================================

/// Command to create a new conversation
class CreateConversationCommand implements Serializable {
  final String title;
  final String? summary;

  const CreateConversationCommand({
    required this.title,
    this.summary,
  });

  @override
  Map<String, Object?> toJson() => {
        'title': title,
        if (summary != null) 'summary': summary,
      };
}

// =============================================================================
// Queries
// =============================================================================

/// Query to get a single conversation by ID
class GetConversationQuery implements Serializable {
  final String id;

  const GetConversationQuery({required this.id});

  @override
  Map<String, Object?> toJson() => {'id': id};
}

// =============================================================================
// DTOs
// =============================================================================

/// Response when creating a conversation (returns only ID)
class CreateConversationResult {
  final String id;

  const CreateConversationResult({required this.id});

  factory CreateConversationResult.fromJson(Map<String, Object?> json) {
    return CreateConversationResult(
      id: json['id'] as String,
    );
  }

  Map<String, Object?> toJson() => {'id': id};
}

/// Conversation message DTO
class ConversationMessageDto {
  final String id;
  final String role;
  final String content;
  final DateTime timestamp;

  const ConversationMessageDto({
    required this.id,
    required this.role,
    required this.content,
    required this.timestamp,
  });

  factory ConversationMessageDto.fromJson(Map<String, Object?> json) {
    return ConversationMessageDto(
      id: json['id'] as String,
      role: json['role'] as String,
      content: json['content'] as String,
      timestamp: DateTime.parse(json['timestamp'] as String),
    );
  }

  Map<String, Object?> toJson() => {
        'id': id,
        'role': role,
        'content': content,
        'timestamp': timestamp.toIso8601String(),
      };
}

/// Full conversation details with messages and executions
class ConversationDto {
  final String id;
  final String title;
  final String? summary;
  final DateTime startedAt;
  final DateTime lastMessageAt;
  final int messageCount;
  final bool isActive;
  final int executionCount;
  final List<ConversationMessageDto> messages;

  const ConversationDto({
    required this.id,
    required this.title,
    this.summary,
    required this.startedAt,
    required this.lastMessageAt,
    required this.messageCount,
    required this.isActive,
    required this.executionCount,
    required this.messages,
  });

  factory ConversationDto.fromJson(Map<String, Object?> json) {
    final List<Object?> messagesList = json['messages'] as List<Object?>? ?? [];
    final List<ConversationMessageDto> messages = messagesList
        .cast<Map<String, Object?>>()
        .map((Map<String, Object?> m) => ConversationMessageDto.fromJson(m))
        .toList();

    return ConversationDto(
      id: json['id'] as String,
      title: json['title'] as String,
      summary: json['summary'] as String?,
      startedAt: DateTime.parse(json['startedAt'] as String),
      lastMessageAt: DateTime.parse(json['lastMessageAt'] as String),
      messageCount: json['messageCount'] as int,
      isActive: json['isActive'] as bool,
      executionCount: json['executionCount'] as int,
      messages: messages,
    );
  }

  Map<String, Object?> toJson() => {
        'id': id,
        'title': title,
        'summary': summary,
        'startedAt': startedAt.toIso8601String(),
        'lastMessageAt': lastMessageAt.toIso8601String(),
        'messageCount': messageCount,
        'isActive': isActive,
        'executionCount': executionCount,
        'messages':
            messages.map((ConversationMessageDto m) => m.toJson()).toList(),
      };
}

/// Conversation list item (lightweight version for lists)
class ConversationListItemDto {
  final String id;
  final String title;
  final String? summary;
  final DateTime startedAt;
  final DateTime lastMessageAt;
  final int messageCount;
  final bool isActive;
  final int executionCount;

  const ConversationListItemDto({
    required this.id,
    required this.title,
    this.summary,
    required this.startedAt,
    required this.lastMessageAt,
    required this.messageCount,
    required this.isActive,
    required this.executionCount,
  });

  factory ConversationListItemDto.fromJson(Map<String, Object?> json) {
    return ConversationListItemDto(
      id: json['id'] as String,
      title: json['title'] as String,
      summary: json['summary'] as String?,
      startedAt: DateTime.parse(json['startedAt'] as String),
      lastMessageAt: DateTime.parse(json['lastMessageAt'] as String),
      messageCount: json['messageCount'] as int,
      isActive: json['isActive'] as bool,
      executionCount: json['executionCount'] as int,
    );
  }

  Map<String, Object?> toJson() => {
        'id': id,
        'title': title,
        'summary': summary,
        'startedAt': startedAt.toIso8601String(),
        'lastMessageAt': lastMessageAt.toIso8601String(),
        'messageCount': messageCount,
        'isActive': isActive,
        'executionCount': executionCount,
      };
}

// =============================================================================
// Extension Methods
// =============================================================================

/// Conversation management endpoints
extension ConversationEndpoint on CqrsApiClient {
  /// Create a new conversation
  ///
  /// Returns the ID of the newly created conversation.
  ///
  /// Example:
  /// ```dart
  /// final result = await client.createConversation(
  ///   CreateConversationCommand(
  ///     title: 'My First Conversation',
  ///     summary: 'Optional summary',
  ///   ),
  /// );
  ///
  /// result.when(
  ///   success: (created) => print('Conversation ID: ${created.id}'),
  ///   error: (error) => print('Error: ${error.message}'),
  /// );
  /// ```
  Future<Result<CreateConversationResult>> createConversation(
    CreateConversationCommand command,
  ) async {
    // This is a special command that returns data (conversation ID)
    // We use executeQuery pattern but with command endpoint
    try {
      final Uri url =
          Uri.parse('${config.baseUrl}/api/command/createConversation');
      final String body = jsonEncode(command.toJson());

      final http.Response response = await http
          .post(
            url,
            headers: config.defaultHeaders,
            body: body,
          )
          .timeout(config.timeout);

      if (response.statusCode >= 200 && response.statusCode < 300) {
        try {
          final Object? json = jsonDecode(response.body);
          final CreateConversationResult result =
              CreateConversationResult.fromJson(json as Map<String, Object?>);
          return ApiSuccess<CreateConversationResult>(result);
        } catch (e) {
          return ApiError<CreateConversationResult>(
            ApiErrorInfo(
              message: 'Failed to parse create conversation response',
              statusCode: response.statusCode,
              type: ApiErrorType.serialization,
              details: e.toString(),
            ),
          );
        }
      } else {
        return ApiError<CreateConversationResult>(
          ApiErrorInfo(
            message: 'Create conversation failed',
            statusCode: response.statusCode,
            type: ApiErrorType.http,
          ),
        );
      }
    } on TimeoutException catch (e) {
      return ApiError<CreateConversationResult>(
        ApiErrorInfo(
          message: 'Request timeout: ${e.message ?? "Operation took too long"}',
          type: ApiErrorType.timeout,
        ),
      );
    } on SocketException catch (e) {
      return ApiError<CreateConversationResult>(
        ApiErrorInfo(
          message: 'Network error: ${e.message}',
          type: ApiErrorType.network,
          details: e.osError?.message,
        ),
      );
    } catch (e) {
      return ApiError<CreateConversationResult>(
        ApiErrorInfo(
          message: 'Unexpected error: $e',
          type: ApiErrorType.unknown,
        ),
      );
    }
  }

  /// Get a single conversation by ID with full details
  ///
  /// Example:
  /// ```dart
  /// final result = await client.getConversation('conversation-uuid');
  ///
  /// result.when(
  ///   success: (conversation) {
  ///     print('Title: ${conversation.title}');
  ///     print('Messages: ${conversation.messageCount}');
  ///     for (final message in conversation.messages) {
  ///       print('${message.role}: ${message.content}');
  ///     }
  ///   },
  ///   error: (error) => print('Error: ${error.message}'),
  ///   );
  /// ```
  Future<Result<ConversationDto>> getConversation(String id) async {
    return executeQuery<ConversationDto>(
      endpoint: 'getConversation',
      query: GetConversationQuery(id: id),
      fromJson: (Object? json) =>
          ConversationDto.fromJson(json as Map<String, Object?>),
    );
  }
}
