// lib/services/network/request.dart

// ignore_for_file: empty_catches

import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:didvan/services/storage/storage.dart';

// ignore: depend_on_referenced_packages
import 'package:http/http.dart' as http;

// ignore: depend_on_referenced_packages
import 'package:http_parser/http_parser.dart' as parser;
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';

class RequestService {
  static String? token; // Made token nullable
  int? statusCode;

  dynamic data(String s) {
    return _body?[s] ?? const {};
  }

  Map<String, dynamic> get result => _body?['result'] ?? const {};

  Map<String, dynamic> get errors => _body?['errors'] ?? const {};

  String errorMessage =
      'خطا! لطفا اتصال اینترنت خود را بررسی و مجددا تلاش نمایید.';

  Map? _body;

  final Map<String, String> _headers = {
    "accept": "*/*",
    "Content-Type": "application/json; charset=UTF-8",
  };
  final String url;
  Map? _requestBody;

  bool isSuccess = false;

  RequestService(
    this.url, {
    Map<String, String>? requestHeaders,
    body,
    bool useAutherization = true,
  }) {
    if (body != null) _requestBody = body;
    if (requestHeaders != null) _headers.addAll(requestHeaders);
    if (useAutherization && token != null) { // Check if token is not null
      _headers.addAll({'Authorization': 'Bearer $token'});
    }
    if (body != null) _requestBody = body;
  }

  String? get _generateBody =>
      _requestBody == null ? null : jsonEncode(_requestBody);

  Future<void> httpGet() async {
    log('req is: $url', name: 'RequestService');
    try {
      final response = await http
          .get(
            Uri.parse(url),
            headers: _headers,
          )
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> post() async {
    try {
      final response = await http
          .post(
            Uri.parse(url),
            body: _generateBody,
            headers: _headers,
          )
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
        (e) {
          throw e;
        },
      );
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> delete() async {
    try {
      final response = await http
          .delete(
            Uri.parse(url),
            body: _generateBody,
            headers: _headers,
          )
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> multipartFilesCreateAssismants({
    required List<XFile>? files,
    required XFile? image,
    required String method,
  }) async {
    try {
      final request = http.MultipartRequest(method, Uri.parse(url));
      _headers.update('Content-Type', (_) => 'multipart/form-data');
      request.headers.addAll(_headers);
      if (_requestBody != null) {
        _requestBody!.forEach((key, value) {
          request.fields.addAll({key.toString(): value.toString()});
        });
      }
      if (files != null) {
        for (var file in files) {
          final length = await file.length();
          final mimeType = lookupMimeType(file.name) ??
              'application/octet-stream'; // Get content type

          request.files.add(
            http.MultipartFile(
              'files',
              file.readAsBytes().asStream(),
              length,
              filename: file.name,
              contentType:
                  parser.MediaType.parse(mimeType), // Use the content type
            ),
          );
        }
      }

      if (image != null) {
        final length = await image.length();
        final mimeType = lookupMimeType(image.name) ??
            'application/octet-stream'; // Get content type

        request.files.add(
          http.MultipartFile(
            'image',
            image.readAsBytes().asStream(),
            length,
            filename: image.name,
            contentType:
                parser.MediaType.parse(mimeType), // Use the content type
          ),
        );
      }

      final streamedResponse = await request
          .send()
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      final response = await http.Response.fromStream(streamedResponse);
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> multipart({
    required dynamic file,
    required String method,
    required String fileName,
    required String fieldName,
    required String mediaFormat,
    required String mediaExtension,
  }) async {
    try {
      final request = http.MultipartRequest(method, Uri.parse(url));
      _headers.update('Content-Type', (_) => 'multipart/form-data');
      request.headers.addAll(_headers);
      final length = await file.length();
      if (_requestBody != null) {
        _requestBody!.forEach((key, value) {
          request.fields.addAll({key.toString(): value.toString()});
        });
      }
      request.files.add(
        http.MultipartFile(
          fieldName,
          file.readAsBytes().asStream(),
          length,
          filename: '$fileName.$mediaExtension',
          contentType: parser.MediaType(mediaFormat, mediaExtension),
        ),
      );
      final streamedResponse = await request
          .send()
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      final response = await http.Response.fromStream(streamedResponse);
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> multipartBytes({
    required Uint8List file,
    required String method,
    required String fileName,
    required String fieldName,
    required String mediaFormat,
    required String mediaExtension,
  }) async {
    try {
      final request = http.MultipartRequest(method, Uri.parse(url));
      _headers.update('Content-Type', (_) => 'multipart/form-data');
      request.headers.addAll(_headers);
      if (_requestBody != null) {
        _requestBody!.forEach((key, value) {
          request.fields.addAll({key.toString(): value.toString()});
        });
      }
      request.files.add(
        http.MultipartFile.fromBytes(
          fieldName,
          file,
          filename: '$fileName.$mediaExtension',
          contentType: parser.MediaType(mediaFormat, mediaExtension),
        ),
      );
      final streamedResponse = await request
          .send()
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      final response = await http.Response.fromStream(streamedResponse);
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> put() async {
    try {
      final response = await http
          .put(
            Uri.parse(url),
            body: _generateBody,
            headers: _headers,
          )
          .timeout(
            const Duration(seconds: 30),
          )
          .catchError(
            (e) => throw e,
          );
      _handleResponse(response);
    } catch (e) {
      _handleError(null);
    }
  }

  Future<void> download(String fileName, String subDirectory) async {
    await Permission.storage.request();
    final response = await http.get(Uri.parse(url));
    StorageService.createFile(
      bytes: response.bodyBytes,
      subDirectory: subDirectory,
      name: fileName,
    );
  }

  void _handleResponse(http.Response? response) {
    statusCode = response?.statusCode;
    if (response != null) {
      if (kDebugMode) {
        print('Response from [$url]: ${response.body}');
      }
    }
    if (_handleError(response)) {
      if (response!.body.isNotEmpty) {
        _body = json.decode(response.body);
      }
    }
    errorMessage = _errorMessageGenerator(response);
  }

  bool _handleError(http.Response? response) {
    if (response == null) {
      log(
        'Connecion failed or request timedout! ("$url")',
        name: 'Request fail error',
      );
      isSuccess = false;
      return false;
    }
    if (response.statusCode != 200) {
      dynamic data;
      if (response.body.isEmpty) {
        data = 'No results!';
      } else if (response.body.contains('<!DOCTYPE html>')) {
        data = response.body;
      } else {
        data = jsonDecode(response.body);
      }
      log(
        'Request to [$url] failed with status code ${response.statusCode} & result : \n $data',
        name: 'Request fail error',
      );
      isSuccess = false;
      return false;
    }
    isSuccess = true;
    return true;
  }

  String _errorMessageGenerator(http.Response? response) {
    String? error;
    if (response != null) {
      if (_body != null) {
        if (result.isNotEmpty) {
          error = result['msg'];
        }
      }
    }
    return error ?? 'خطا! لطفا اتصال اینترنت خود را بررسی و مجددا تلاش نمایید.';
  }
}
