import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:hl_lieferservice/dto/note_get_response.dart';
import 'package:hl_lieferservice/feature/delivery/detail/exceptions.dart';
import 'package:hl_lieferservice/services/erpframe.dart';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

import '../../../../dto/basic_response.dart';
import '../../../../dto/note_add_response.dart';
import '../../../../dto/note_template_response.dart';
import '../../../../model/delivery.dart';
import '../../../../util.dart';
import '../../../authentication/exceptions.dart';

class NoteService {
  Future<void> deleteNote(int noteId) async {
    try {
      var response = await http.post(
        urlBuilder("_web_deleteNote"),
        headers: getSessionOrThrow(),
        body: {"id": noteId.toString()},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      debugPrint("NOTE DELETE: ${response.body}");
      BasicResponseDTO responseDto = BasicResponseDTO.fromJson(responseJson);

      if (responseDto.succeeded == true) {
        return;
      } else {
        throw responseDto.message;
      }
    } catch (e, st) {
      debugPrint("ERROR WHILE DELETING NOTE $noteId");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<void> editNote(Note newNote) async {
    try {
      var response = await http.post(
        urlBuilder("_web_editNote"),
        headers: getSessionOrThrow(),
        body: {"id": newNote.id.toString(), "note": newNote.content},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      BasicResponseDTO responseDto = BasicResponseDTO.fromJson(responseJson);

      if (responseDto.succeeded == true) {
        return;
      } else {
        throw responseDto.message;
      }
    } catch (e, st) {
      debugPrint("ERROR WHILE EDITING NOTE ${newNote.id}");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<List<NoteTemplate>> getNoteTemplates() async {
    try {
      var response = await http.post(
        urlBuilder("_web_getNoteTemplates"),
        headers: getSessionOrThrow(),
        body: {},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      NoteTemplateResponseDTO responseDto = NoteTemplateResponseDTO.fromJson(
        responseJson,
      );

      if (responseDto.succeeded == true) {
        return responseDto.notes.map(NoteTemplate.fromDTO).toList();
      } else {
        throw responseDto.message;
      }
    } catch (e, st) {
      debugPrint("ERROR WHILE GETTING NOTE TEMPLATES");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<(List<Note>, List<ImageNote>)> getNotes(String deliveryId) async {
    try {
      var response = await http.post(
        urlBuilder("_web_getNotes"),
        headers: getSessionOrThrow(),
        body: {"delivery_id": deliveryId},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      NoteGetResponseDTO responseDto = NoteGetResponseDTO.fromJson(
        responseJson,
      );

      if (responseDto.succeeded == true) {
        List<ImageNote> imageNotes =
            responseDto.images
                .map((imageNoteDto) => ImageNote.fromDTO(imageNoteDto))
                .toList();

        final images = await downloadImages(imageNotes.map((note) => note.url).toList());
        for (var (index, note) in imageNotes.indexed) {
          note.data = await images[index];
        }

        return (
            responseDto.notes
                .map((noteDto) => Note.fromDto(noteDto))
                .toList(),
            imageNotes
        );
      } else {
        throw responseDto.message;
      }
    } catch (e, st) {
      debugPrint("ERROR WHILE GETTING NOTES");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<Note> addNote(String note, int deliveryId) async {
    try {
      var response = await http.post(
        urlBuilder("_web_addNote"),
        headers: getSessionOrThrow(),
        body: {"receipt_id": deliveryId.toString(), "note": note},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      debugPrint(responseJson.toString());
      NoteAddResponseDTO responseDto = NoteAddResponseDTO.fromJson(
        responseJson,
      );

      if (responseDto.succeeded == true) {
        return Note.fromDto(responseDto.note!);
      } else {
        debugPrint("ERROR: ${responseDto.message}");
        throw responseDto.message;
      }
    } catch (e) {
      rethrow;
    }
  }

  Future<String> uploadImage(
    String deliveryId,
    String filename,
    Uint8List bytes,
    String? mimeType,
  ) async {
    try {
      var config = getConfig();
      var basePath = "${config.backendUrl}/v1/uploadFile";
      var response = await http.get(
        Uri.parse(basePath),
        headers: getSessionOrThrow(),
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> jsonResponse = jsonDecode(response.body);
      debugPrint("GET UPLOADID : ${response.body}");

      if (!jsonResponse.containsKey("data")) {
        debugPrint("No data structure in uploadFile request");
        debugPrint("RAW RESPONSE: ${response.body}");
        throw NoteImageAddException();
      }

      Map<String, dynamic> data = jsonResponse["data"];

      if (!data.containsKey("uploadId")) {
        debugPrint("No data.uploadId structure in uploadFile request");
        debugPrint("RAW RESPONSE: ${response.body}");
        throw NoteImageAddException();
      }

      String uploadId = data["uploadId"];
      http.MultipartRequest request = http.MultipartRequest(
        "POST",
        Uri.parse("$basePath/$uploadId"),
      );

      HashMap<String, String> header = HashMap();
      header["Content-Type"] = "multipart/form-data";
      header.addAll(getSessionOrThrow());

      request.headers.addAll(header);
      request.files.add(
        http.MultipartFile.fromBytes(
          "file",
          bytes,
          filename: filename,
          contentType: MediaType.parse(mimeType ?? "application/octet-stream"),
        ),
      );

      http.Response fileUploadResponse = await http.Response.fromStream(
        await request.send(),
      );
      Map<String, dynamic> fileUploadResponseJson = jsonDecode(
        fileUploadResponse.body,
      );

      debugPrint("UPLOAD IMAGE RESPONSE: ${fileUploadResponse.body}");

      if (fileUploadResponseJson["status"]["internalStatus"] != "0") {
        debugPrint("Failed to upload image");
        debugPrint("RAW: ${fileUploadResponseJson.toString()}");
        throw NoteImageAddException();
      }

      var fileCommitResponse = await http.patch(
        Uri.parse("$basePath/$uploadId"),
        headers: getSessionOrThrow(),
      );
      debugPrint("FILE COMMIT BODY: ${fileCommitResponse.body}");
      var fileCommitResponseJson = jsonDecode(fileCommitResponse.body);

      return fileCommitResponseJson["data"]["~ObjectID"];
    } catch (e, st) {
      debugPrint("An error occured:");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<List<Future<Uint8List>>> downloadImages(List<String> urls) async {
    try {
      LocalDocuFrameConfiguration config = getConfig();

      return urls.map((url) async {
        final response = await http.get(
          Uri.parse("${config.backendUrl}$url"),
          headers: getSessionOrThrow(),
        );
        if (response.statusCode == HttpStatus.unauthorized) {
          throw UserUnauthorized();
        }
        return response.bodyBytes;
      }).toList();
    } catch (e, st) {
      debugPrint("An error occured:");
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }

  Future<void> removeImage(String oid) async {
    try {
      var response = await http.post(
        urlBuilder("_web_removeImage"),
        headers: getSessionOrThrow(),
        body: {"oid": oid},
      );

      if (response.statusCode == HttpStatus.unauthorized) {
        throw UserUnauthorized();
      }

      Map<String, dynamic> responseJson = jsonDecode(response.body);
      debugPrint(oid);
      debugPrint(responseJson.toString());

      BasicResponseDTO responseDto = BasicResponseDTO.fromJson(responseJson);

      if (responseDto.succeeded == true) {
        return;
      } else {
        debugPrint("ERROR: ${responseDto.message}");
        throw responseDto.message;
      }
    } catch (e, st) {
      debugPrint("$e");
      debugPrint(st.toString());

      rethrow;
    }
  }
}
