/// Authentication Remote Data Source
///
/// Handles all authentication-related API calls.
library;

import 'package:dio/dio.dart';
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/features/auth/data/models/auth_session_model.dart';
import 'package:worker/features/auth/domain/entities/city.dart';
import 'package:worker/features/auth/domain/entities/customer_group.dart';

/// Authentication Remote Data Source
///
/// Provides methods for:
/// - Getting session (CSRF token and SID)
/// - Fetching cities for registration
/// - Fetching customer groups (roles) for registration
/// - User registration
class AuthRemoteDataSource {
  final Dio _dio;

  AuthRemoteDataSource(this._dio);

  /// Get Session
  ///
  /// Fetches session data including SID and CSRF token.
  /// This should be called before making authenticated requests.
  ///
  /// API: POST /api/method/dbiz_common.dbiz_common.api.auth.get_session
  Future<GetSessionResponse> getSession() async {
    try {
      final response = await _dio.post<Map<String, dynamic>>(
        '/api/method/dbiz_common.dbiz_common.api.auth.get_session',
        data: '',
      );

      if (response.statusCode == 200 && response.data != null) {
        return GetSessionResponse.fromJson(response.data!);
      } else {
        throw ServerException(
          'Failed to get session: ${response.statusCode}',
        );
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        throw const UnauthorizedException();
      } else if (e.response?.statusCode == 404) {
        throw NotFoundException('Session endpoint not found');
      } else {
        throw NetworkException(
          e.message ?? 'Failed to get session',
        );
      }
    } catch (e) {
      throw ServerException('Unexpected error: $e');
    }
  }

  /// Login
  ///
  /// Authenticates user with phone number.
  /// Requires existing session (CSRF token and Cookie).
  /// Returns new session with user credentials.
  ///
  /// API: POST /api/method/building_material.building_material.api.auth.login
  /// Body: { "username": "phone", "googleid": null, "facebookid": null, "zaloid": null }
  ///
  /// Response includes new sid and csrf_token for authenticated user.
  Future<GetSessionResponse> login({
    required String phone,
    required String csrfToken,
    required String sid,
    String? password, // Reserved for future use
  }) async {
    try {
      final response = await _dio.post<Map<String, dynamic>>(
        '/api/method/building_material.building_material.api.auth.login',
        data: {
          'username': phone,
          'googleid': null,
          'facebookid': null,
          'zaloid': null,
          // Password field reserved for future use
          // 'password': password,
        },
        options: Options(
          headers: {
            'X-Frappe-Csrf-Token': csrfToken,
            'Cookie': 'sid=$sid',
            'Content-Type': 'application/json',
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        return GetSessionResponse.fromJson(response.data!);
      } else {
        throw ServerException(
          'Login failed: ${response.statusCode}',
        );
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        throw const UnauthorizedException('Invalid credentials');
      } else if (e.response?.statusCode == 404) {
        throw NotFoundException('Login endpoint not found');
      } else {
        throw NetworkException(
          e.message ?? 'Failed to login',
        );
      }
    } catch (e) {
      throw ServerException('Unexpected error during login: $e');
    }
  }

  /// Get Cities
  ///
  /// Fetches list of cities/provinces for address selection.
  /// Requires authenticated session (CSRF token and Cookie).
  ///
  /// API: POST /api/method/frappe.client.get_list
  /// DocType: City
  Future<List<City>> getCities({
    required String csrfToken,
    required String sid,
  }) async {
    try {
      final response = await _dio.post<Map<String, dynamic>>(
        '/api/method/frappe.client.get_list',
        data: {
          'doctype': 'City',
          'fields': ['city_name', 'name', 'code'],
          'limit_page_length': 0,
        },
        options: Options(
          headers: {
            'X-Frappe-Csrf-Token': csrfToken,
            'Cookie': 'sid=$sid',
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        final message = response.data!['message'];
        if (message is List) {
          return message
              .map((json) => City.fromJson(json as Map<String, dynamic>))
              .toList();
        } else {
          throw ServerException('Invalid response format for cities');
        }
      } else {
        throw ServerException(
          'Failed to get cities: ${response.statusCode}',
        );
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        throw const UnauthorizedException();
      } else if (e.response?.statusCode == 404) {
        throw NotFoundException('Cities endpoint not found');
      } else {
        throw NetworkException(
          e.message ?? 'Failed to get cities',
        );
      }
    } catch (e) {
      throw ServerException('Unexpected error: $e');
    }
  }

  /// Get Customer Groups (Roles)
  ///
  /// Fetches list of customer groups for user role selection.
  /// Requires authenticated session (CSRF token and Cookie).
  ///
  /// API: POST /api/method/frappe.client.get_list
  /// DocType: Customer Group
  Future<List<CustomerGroup>> getCustomerGroups({
    required String csrfToken,
    required String sid,
  }) async {
    try {
      final response = await _dio.post<Map<String, dynamic>>(
        '/api/method/frappe.client.get_list',
        data: {
          'doctype': 'Customer Group',
          'fields': ['customer_group_name', 'name', 'value'],
          'filters': {
            'is_group': 0,
            'is_active': 1,
            'customer': 1,
          },
          'limit_page_length': 0,
          'order_by': 'custom_line_no asc'
        },
        options: Options(
          headers: {
            'X-Frappe-Csrf-Token': csrfToken,
            'Cookie': 'sid=$sid',
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        final message = response.data!['message'];
        if (message is List) {
          return message
              .map((json) =>
                  CustomerGroup.fromJson(json as Map<String, dynamic>))
              .toList();
        } else {
          throw ServerException('Invalid response format for customer groups');
        }
      } else {
        throw ServerException(
          'Failed to get customer groups: ${response.statusCode}',
        );
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        throw const UnauthorizedException();
      } else if (e.response?.statusCode == 404) {
        throw NotFoundException('Customer groups endpoint not found');
      } else {
        throw NetworkException(
          e.message ?? 'Failed to get customer groups',
        );
      }
    } catch (e) {
      throw ServerException('Unexpected error: $e');
    }
  }

  /// Register User
  ///
  /// Registers a new user with the provided information.
  /// Requires authenticated session (CSRF token and Cookie).
  ///
  /// API: POST /api/method/building_material.building_material.api.user.register
  Future<Map<String, dynamic>> register({
    required String csrfToken,
    required String sid,
    required String fullName,
    required String phone,
    required String email,
    required String customerGroupCode,
    required String cityCode,
    String? companyName,
    String? taxCode,
    String? idCardFrontBase64,
    String? idCardBackBase64,
    List<String>? certificatesBase64,
  }) async {
    try {
      final response = await _dio.post<Map<String, dynamic>>(
        '/api/method/building_material.building_material.api.user.register',
        data: {
          'full_name': fullName,
          'phone': phone,
          'email': email,
          'customer_group_code': customerGroupCode,
          'city_code': cityCode,
          'company_name': companyName,
          'tax_code': taxCode,
          'id_card_front_base64': idCardFrontBase64,
          'id_card_back_base64': idCardBackBase64,
          'certificates_base64': certificatesBase64 ?? [],
        },
        options: Options(
          headers: {
            'X-Frappe-Csrf-Token': csrfToken,
            'Cookie': 'sid=$sid',
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        return response.data!;
      } else {
        throw ServerException(
          'Failed to register: ${response.statusCode}',
        );
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 401) {
        throw const UnauthorizedException();
      } else if (e.response?.statusCode == 400) {
        throw ValidationException(
          e.response?.data?['message'] as String? ?? 'Validation error',
        );
      } else if (e.response?.statusCode == 404) {
        throw NotFoundException('Register endpoint not found');
      } else {
        throw NetworkException(
          e.message ?? 'Failed to register',
        );
      }
    } catch (e) {
      throw ServerException('Unexpected error: $e');
    }
  }
}
