/// Address Remote Data Source
///
/// Handles API calls to Frappe ERPNext address endpoints.
library;

import 'package:dio/dio.dart';
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/features/account/data/models/address_model.dart';

/// Address Remote Data Source
///
/// Provides methods to interact with address API endpoints.
/// Online-only approach - no offline caching.
class AddressRemoteDataSource {
  final Dio _dio;

  AddressRemoteDataSource(this._dio);

  /// Get list of addresses
  ///
  /// Fetches all addresses for the authenticated user.
  /// Optionally filter by default address.
  ///
  /// API: GET /api/method/building_material.building_material.api.address.get_list
  Future<List<AddressModel>> getAddresses({
    int limitStart = 0,
    int limitPageLength = 0,
    bool? isDefault,
  }) async {
    try {
      _debugPrint('Fetching addresses list...');

      final response = await _dio.post(
        '/api/method/building_material.building_material.api.address.get_list',
        data: {
          'limit_start': limitStart,
          'limit_page_length': limitPageLength,
          if (isDefault != null) 'is_default': isDefault,
        },
      );

      if (response.statusCode == 200) {
        final data = response.data;
        _debugPrint('Response data: $data');

        // Extract addresses from response
        if (data is Map<String, dynamic> && data.containsKey('message')) {
          final message = data['message'];
          _debugPrint('Message type: ${message.runtimeType}');

          // Handle array response
          if (message is List) {
            _debugPrint('Parsing ${message.length} addresses from list');
            final addresses = <AddressModel>[];
            for (var i = 0; i < message.length; i++) {
              try {
                final item = message[i] as Map<String, dynamic>;
                _debugPrint('Parsing address $i: $item');
                final address = AddressModel.fromJson(item);
                addresses.add(address);
              } catch (e) {
                _debugPrint('Error parsing address $i: $e');
                rethrow;
              }
            }

            _debugPrint('Fetched ${addresses.length} addresses');
            return addresses;
          }

          // Handle object with data field
          if (message is Map<String, dynamic> && message.containsKey('data')) {
            final dataList = message['data'] as List;
            _debugPrint('Parsing ${dataList.length} addresses from data field');
            final addresses = <AddressModel>[];
            for (var i = 0; i < dataList.length; i++) {
              try {
                final item = dataList[i] as Map<String, dynamic>;
                _debugPrint('Parsing address $i: $item');
                final address = AddressModel.fromJson(item);
                addresses.add(address);
              } catch (e) {
                _debugPrint('Error parsing address $i: $e');
                rethrow;
              }
            }

            _debugPrint('Fetched ${addresses.length} addresses');
            return addresses;
          }
        }

        throw const ServerException('Invalid response format');
      } else {
        throw ServerException(
          'Failed to fetch addresses: ${response.statusCode}',
        );
      }
    } catch (e) {
      _debugPrint('Error fetching addresses: $e');
      rethrow;
    }
  }

  /// Create or update address
  ///
  /// If name is provided (not empty), updates existing address.
  /// If name is null/empty, creates new address.
  ///
  /// Per API docs: When name field is null/empty, the API creates a new address.
  /// When name has a value, the API updates the existing address.
  ///
  /// API: POST /api/method/building_material.building_material.api.address.update
  Future<AddressModel> saveAddress(AddressModel address) async {
    try {
      final isUpdate = address.name.isNotEmpty;
      _debugPrint(
        isUpdate
            ? 'Updating address: ${address.name}'
            : 'Creating new address',
      );

      // toJson() already handles setting name to null for creation
      final data = address.toJson();
      _debugPrint('Request data: $data');

      final response = await _dio.post(
        '/api/method/building_material.building_material.api.address.update',
        data: data,
      );

      if (response.statusCode == 200) {
        final data = response.data;
        _debugPrint('Response data: $data');

        // Check for API error response (even with 200 status)
        if (data is Map<String, dynamic> && data.containsKey('message')) {
          final message = data['message'];

          // Check for error response format
          if (message is Map<String, dynamic> && message.containsKey('error')) {
            final error = message['error'] as String;
            _debugPrint('API error: $error');
            throw ServerException(error);
          }

          // Handle direct address object
          if (message is Map<String, dynamic>) {
            final savedAddress = AddressModel.fromJson(message);
            _debugPrint('Address saved: ${savedAddress.name}');
            return savedAddress;
          }

          // Handle nested data
          if (message is Map<String, dynamic> && message.containsKey('data')) {
            final savedAddress =
                AddressModel.fromJson(message['data'] as Map<String, dynamic>);
            _debugPrint('Address saved: ${savedAddress.name}');
            return savedAddress;
          }
        }

        throw const ServerException('Invalid response format');
      } else {
        throw ServerException(
          'Failed to save address: ${response.statusCode}',
        );
      }
    } catch (e) {
      _debugPrint('Error saving address: $e');
      rethrow;
    }
  }

  /// Delete address
  ///
  /// Note: API endpoint for delete not provided in docs.
  /// This is a placeholder - adjust when endpoint is available.
  Future<void> deleteAddress(String name) async {
    try {
      _debugPrint('Deleting address: $name');

      // TODO: Update with actual delete endpoint when available
      final response = await _dio.post(
        '/api/method/building_material.building_material.api.address.delete',
        data: {'name': name},
      );

      if (response.statusCode == 200) {
        _debugPrint('Address deleted: $name');
        return;
      } else {
        throw ServerException(
          'Failed to delete address: ${response.statusCode}',
        );
      }
    } catch (e) {
      _debugPrint('Error deleting address: $e');
      rethrow;
    }
  }

  /// Debug print helper
  void _debugPrint(String message) {
    // ignore: avoid_print
    print('[AddressRemoteDataSource] $message');
  }
}
