/// Submissions Remote Data Source
///
/// Handles remote API calls for project submissions.
library;

import 'package:dio/dio.dart';
import 'package:worker/core/constants/api_constants.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/features/projects/data/models/project_progress_model.dart';
import 'package:worker/features/projects/data/models/project_status_model.dart';
import 'package:worker/features/projects/data/models/project_submission_model.dart';
import 'package:worker/features/projects/data/models/project_submission_request.dart';

/// Submissions Remote Data Source
///
/// Interface for remote project submission operations.
abstract class SubmissionsRemoteDataSource {
  /// Fetch project status list from API
  Future<List<ProjectStatusModel>> getProjectStatusList();

  /// Fetch project progress list from API (construction stages)
  Future<List<ProjectProgressModel>> getProjectProgressList();

  /// Fetch all submissions from remote API
  Future<List<ProjectSubmissionModel>> getSubmissions({
    int limitStart = 0,
    int limitPageLength = 0,
  });

  /// Fetch project detail by name
  /// Returns the full project detail as a model
  Future<ProjectSubmissionModel> getSubmissionDetail(String name);

  /// Create or update a project submission
  /// Returns the project name (ID) from the API response
  Future<String> saveSubmission(ProjectSubmissionRequest request);

  /// Upload a file for a project submission
  /// [projectName] is the project ID returned from saveSubmission
  /// [filePath] is the local path to the file
  /// Returns the uploaded file URL
  Future<String> uploadProjectFile({
    required String projectName,
    required String filePath,
  });

  /// Delete a file from a project submission
  /// [fileId] is the file ID to delete
  /// [projectName] is the project name (docname)
  Future<void> deleteProjectFile({
    required String fileId,
    required String projectName,
  });
}

/// Submissions Remote Data Source Implementation
///
/// Uses Frappe API endpoints for project submissions.
class SubmissionsRemoteDataSourceImpl implements SubmissionsRemoteDataSource {
  const SubmissionsRemoteDataSourceImpl(this._dioClient);

  final DioClient _dioClient;

  /// Get project status list
  ///
  /// Calls: POST /api/method/building_material.building_material.api.project.get_project_status_list
  /// Body: { "limit_start": 0, "limit_page_length": 0 }
  /// Returns: List of project statuses with labels and colors
  @override
  Future<List<ProjectStatusModel>> getProjectStatusList() async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getProjectStatusList}',
        data: <String, dynamic>{
          'limit_start': 0,
          'limit_page_length': 0,
        },
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getProjectStatusList API');
      }

      // API returns: { "message": [...] }
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getProjectStatusList response');
      }

      final List<dynamic> statusList = message as List<dynamic>;
      return statusList
          .map((json) =>
              ProjectStatusModel.fromJson(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      throw Exception('Failed to get project status list: $e');
    }
  }

  /// Get project progress list (construction stages)
  ///
  /// Calls: POST /api/method/frappe.client.get_list
  /// Body: {
  ///   "doctype": "Progress of construction",
  ///   "fields": ["name", "status"],
  ///   "order_by": "number_of_display asc",
  ///   "limit_page_length": 0
  /// }
  /// Returns: List of construction progress stages
  @override
  Future<List<ProjectProgressModel>> getProjectProgressList() async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}',
        data: {
          'doctype': 'Progress of construction',
          'fields': ['name', 'status'],
          'order_by': 'number_of_display asc',
          'limit_page_length': 0,
        },
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getProjectProgressList API');
      }

      // API returns: { "message": [...] }
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getProjectProgressList response');
      }

      final List<dynamic> progressList = message as List<dynamic>;
      return progressList
          .map((json) =>
              ProjectProgressModel.fromJson(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      throw Exception('Failed to get project progress list: $e');
    }
  }

  /// Get list of project submissions
  ///
  /// Calls: POST /api/method/building_material.building_material.api.project.get_list
  /// Body: { "limit_start": 0, "limit_page_length": 0 }
  /// Returns: List of project submissions
  @override
  Future<List<ProjectSubmissionModel>> getSubmissions({
    int limitStart = 0,
    int limitPageLength = 0,
  }) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getProjectList}',
        data: {
          'limit_start': limitStart,
          'limit_page_length': limitPageLength,
        },
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getProjectList API');
      }

      // API returns: { "message": [...] }
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getProjectList response');
      }

      final List<dynamic> submissionsList = message as List<dynamic>;
      return submissionsList
          .map((json) =>
              ProjectSubmissionModel.fromJson(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      throw Exception('Failed to get project submissions: $e');
    }
  }

  /// Get project detail by name
  ///
  /// Calls: POST /api/method/building_material.building_material.api.project.get_detail
  /// Body: { "name": "#DA00011" }
  /// Response: { "message": { "success": true, "data": {...} } }
  /// Returns: Full project detail as model
  @override
  Future<ProjectSubmissionModel> getSubmissionDetail(String name) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getProjectDetail}',
        data: {'name': name},
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getProjectDetail API');
      }

      // API returns: { "message": { "success": true, "data": {...} } }
      final message = data['message'] as Map<String, dynamic>?;
      if (message == null) {
        throw Exception('No message field in getProjectDetail response');
      }

      final detailData = message['data'] as Map<String, dynamic>?;
      if (detailData == null) {
        throw Exception('No data field in getProjectDetail response');
      }

      return ProjectSubmissionModel.fromJson(detailData);
    } catch (e) {
      throw Exception('Failed to get project detail: $e');
    }
  }

  /// Save (create/update) a project submission
  ///
  /// Calls: POST /api/method/building_material.building_material.api.project.save
  /// Body: ProjectSubmissionRequest.toJson()
  /// Returns: Project name (ID) from response
  @override
  Future<String> saveSubmission(ProjectSubmissionRequest request) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.saveProject}',
        data: request.toJson(),
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from saveProject API');
      }

      // Check for error in response
      if (data['exc_type'] != null || data['exception'] != null) {
        final errorMessage =
            data['_server_messages'] ?? data['exception'] ?? 'Unknown error';
        throw Exception('API error: $errorMessage');
      }

      // Extract project name from response
      // Response format: { "message": { "success": true, "data": { "name": "#DA00007" } } }
      final message = data['message'] as Map<String, dynamic>?;
      if (message == null) {
        throw Exception('No message in saveProject response');
      }

      final messageData = message['data'] as Map<String, dynamic>?;
      if (messageData == null || messageData['name'] == null) {
        throw Exception('No project name in saveProject response');
      }

      return messageData['name'] as String;
    } catch (e) {
      throw Exception('Failed to save project submission: $e');
    }
  }

  /// Upload a file for a project submission
  ///
  /// Calls: POST /api/method/upload_file
  /// Form-data: file, is_private, folder, doctype, docname, optimize
  /// Returns: Uploaded file URL
  @override
  Future<String> uploadProjectFile({
    required String projectName,
    required String filePath,
  }) async {
    try {
      final fileName = filePath.split('/').last;
      final formData = FormData.fromMap({
        'file': await MultipartFile.fromFile(filePath, filename: fileName),
        'is_private': '0',
        'folder': 'Home/Attachments',
        'doctype': 'Architectural Project',
        'docname': projectName,
        'optimize': 'true',
      });

      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.uploadFile}',
        data: formData,
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from uploadFile API');
      }

      // Check for error in response
      if (data['exc_type'] != null || data['exception'] != null) {
        final errorMessage =
            data['_server_messages'] ?? data['exception'] ?? 'Unknown error';
        throw Exception('API error: $errorMessage');
      }

      // Extract file URL from response
      // Response format: { "message": { "file_url": "/files/...", ... } }
      final message = data['message'];
      if (message == null || message['file_url'] == null) {
        throw Exception('No file URL in uploadFile response');
      }

      return message['file_url'] as String;
    } catch (e) {
      throw Exception('Failed to upload project file: $e');
    }
  }

  /// Delete a file from a project submission
  ///
  /// Calls: POST /api/method/frappe.desk.form.utils.remove_attach
  /// Form-data: fid, dt, dn
  @override
  Future<void> deleteProjectFile({
    required String fileId,
    required String projectName,
  }) async {
    try {
      final formData = FormData.fromMap({
        'fid': fileId,
        'dt': 'Architectural Project',
        'dn': projectName,
      });

      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.removeProjectFile}',
        data: formData,
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from deleteProjectFile API');
      }

      // Check for error in response
      if (data['exc_type'] != null || data['exception'] != null) {
        final errorMessage =
            data['_server_messages'] ?? data['exception'] ?? 'Unknown error';
        throw Exception('API error: $errorMessage');
      }
    } catch (e) {
      throw Exception('Failed to delete project file: $e');
    }
  }
}
