import 'dart:convert';
import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:intl/intl.dart';
import 'package:unit2/model/passo/floor_sketch.dart';
import 'package:unit2/sevices/offline/offline_passo/admin/sql_services/sql_services.dart';
import 'package:unit2/sevices/passo/building/building_services.dart';
import '../../../../../model/offline/offline_profile.dart';
import '../../../../../model/passo/additional_items.dart';
import '../../../../../model/passo/bldg_loc.dart';
import '../../../../../model/passo/building_and_structure.dart';
import '../../../../../model/passo/general_description.dart';
import '../../../../../model/passo/land_ref.dart';
import '../../../../../model/passo/property_assessment.dart';
import '../../../../../model/passo/property_info.dart';
import '../../../../../model/passo/structureMaterial.dart';
import '../../../../../model/passo/todo.dart';
import '../../../../../model/profile/basic_information/primary-information.dart';
import '../../../../../sevices/offline/offline_passo/building/property_owner_info_service.dart';
import 'package:http/http.dart';

import 'package:path/path.dart';
//  as http;

import '../../../../../utils/urls.dart';
part 'crud_event.dart';
part 'crud_state.dart';

class CrudBloc extends Bloc<CrudEvent, CrudState> {
  CrudBloc() : super(CrudInitial()) {
    List<PropertyInfo> propertyOwner = [];
    on<AddTodo>((event, emit) async {
      try {
        PropertyInfo ownerInfo;
        ownerInfo = await SQLServices.instance.createBldgOwner(
          PropertyInfo(
              transCode: event.transCode,
              tdn: event.tdn,
              pin: event.pin,
              fname: event.fname,
              mname: event.mname,
              lname: event.lname,
              bday: event.bday,
              address: event.address,
              telno: event.telno,
              tin: event.tin,
              adminUser: event.adminUser,
              adminAddress: event.adminAddress,
              adminTelno: event.adminTelno,
              adminTin: event.adminTin,
              faasType: event.faasType,
              assessedById: event.assessedById,
              assessedByName: event.assessedByName,
              dateCreated: event.dateCreated,
              dateModified: event.dateModified,
              genCode: event.genCode),
        );

        propertyOwner.add(ownerInfo);

        emit(PropertyInfoLoaded(propertyInfos: propertyOwner));
      } catch (e) {
        emit(PropertyOwnerInfoErrorState(errorMessage: 'Failed to add todo'));
        print('Error: $e');
        // You might want to throw or log the error, or take other appropriate actions

        // If you want to rethrow the error, uncomment the following line
        // throw e;
      }
    });

    on<UpdatePropertyOwnerInfo>((event, emit) async {
      await SQLServices.instance.updateBldgOwner(event.id, event.propertyInfo);
    });

    on<FetchTodos>((event, emit) async {
      emit(PropertyOwnerInfoLoading());
      propertyOwner = await SQLServices.instance.readAllBldgOwner();
      emit(PropertyInfoLoaded(propertyInfos: propertyOwner));
    });

    // on<FetchSpecificTodo>((event, emit) async {
    //   Prop todo = await PropertyOwnerInfoServices.instance.readTodo(id: event.id);
    //   emit(DisplaySpecificTodo(todo: todo));
    // });

    on<DeleteTodo>((event, emit) async {
      await SQLServices.instance.deleteBldgOwner(id: event.id);
      // Directly fetch and emit new data rather than triggering another event
      try {
        emit(PropertyOwnerInfoLoading());
        propertyOwner.clear(); // Clear the current list
        propertyOwner =
            await SQLServices.instance.readAllBldgOwner(); // Refetch data
        emit(PropertyInfoLoaded(
            propertyInfos: propertyOwner)); // Emit new state with updated data
      } catch (error) {
        // emit((error.toString()));  // Handle potential errors
        print(error.toString());
      }
    });

    Future<Map<String, dynamic>> _prepareBuildingDetails(
        UploadBuildingFaas event, PropertyInfo infos) async {
      // Fetch data
      List<Map<String, dynamic>> genDesc =
          await SQLServices.instance.getGeneralDescription(infos.id);
      List<Map<String, dynamic>> loc =
          await SQLServices.instance.getLocation(infos.id);
      List<Map<String, dynamic>> landRef =
          await SQLServices.instance.getLandRef(infos.id);
      List<Map<String, dynamic>> assessment =
          await SQLServices.instance.getBldgAssessment(infos.id);
      List<Map<String, dynamic>> strucMat =
          await SQLServices.instance.getStructuralMaterials(infos.id);
      List<Map<String, dynamic>> addItems =
          await SQLServices.instance.getAdditionalItems(infos.id);
      List<Map<String, dynamic>> bldgStructure =
          await SQLServices.instance.getBuildingAndStructure(infos.id);

      // Parse data
      GeneralDesc firstGenDesc = GeneralDesc.fromJson2(genDesc.first);
      BldgLoc firstLoc = BldgLoc.fromJson2(loc.first);
      LandRef firstLandRef = LandRef.fromJson2(landRef.first);
      PropertyAssessment firstAssess =
          PropertyAssessment.fromJson2(assessment.first);
      StructureMaterials firstStructMat =
          StructureMaterials.fromJson2(strucMat.first);

      // Prepare details
      DateTime dateIssued = DateTime.parse(firstGenDesc.dateIssued!);
      final details = {
        "assessed_by_id": event.offlineProfile.id.toString(),
        "assessed_by_name": event.offlineProfile.firstName,
        "date_created": "{{currentTimestamp}}",
        "date_modified": "{{currentTimestamp}}",
        "trans_code": '08887',
        "tdn": infos.tdn,
        "pin": infos.pin,
        "fname": infos.fname,
        "mname": infos.mname,
        "lname": infos.lname,
        "bday": infos.bday,
        "address": infos.address,
        "telno": infos.telno,
        "tin": infos.tin,
        "admin_user": infos.adminUser,
        "admin_address": infos.adminAddress,
        "admin_telno": infos.adminTelno,
        "admin_tin": infos.adminTin,
        "faas_type": infos.faasType,
        "gen_code": "5TH",
        "bldgappr_location.date_created": "{{currentTimestamp}}",
        "bldgappr_location.date_modified": "{{currentTimestamp}}",
        "bldgappr_location.street": firstLoc.street,
        "bldgappr_location.barangay": firstLoc.barangay,
        "bldgappr_location.municipality": firstLoc.municipality,
        "bldgappr_location.province": firstLoc.province,
        "bldgappr_location.gen_code": "5TH",
        "bldgappr_landref.date_created": "{{currentTimestamp}}",
        "bldgappr_landref.date_modified": "{{currentTimestamp}}",
        "bldgappr_landref.owner": firstLandRef.owner,
        "bldgappr_landref.cloa_no": firstLandRef.cloaNo,
        "bldgappr_landref.lot_no": firstLandRef.lotNo,
        "bldgappr_landref.tdn": firstLandRef.tdn,
        "bldgappr_landref.area": firstLandRef.area,
        "bldgappr_landref.survey_no": firstLandRef.surveyNo,
        "bldgappr_landref.blk_no": firstLandRef.blkNo,
        "bldgappr_landref.gen_code": "5TH",
        "bldgappr_generaldesc.date_created": "{{currentTimestamp}}",
        "bldgappr_generaldesc.date_modified": "{{currentTimestamp}}",
        "bldgappr_generaldesc.bldg_kind": firstGenDesc.bldgKind,
        "bldgappr_generaldesc.struc_type": firstGenDesc.strucType,
        "bldgappr_generaldesc.bldg_permit": firstGenDesc.bldgPermit,
        "bldgappr_generaldesc.date_issued": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstGenDesc.dateIssued!)),
        "bldgappr_generaldesc.cct": null,
        "bldgappr_generaldesc.cert_completion_issued": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstGenDesc.certCompletionIssued!)),
        "bldgappr_generaldesc.cert_occupancy_issued": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstGenDesc.certOccupancyIssued!)),
        "bldgappr_generaldesc.date_completed": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstGenDesc.dateCompleted!)),
        "bldgappr_generaldesc.date_occupied": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstGenDesc.dateOccupied!)),
        "bldgappr_generaldesc.bldg_age": firstGenDesc.bldgAge,
        "bldgappr_generaldesc.no_storeys": firstGenDesc.noStoreys,
        "bldgappr_generaldesc.area_1stfloor": firstGenDesc.area1Stfloor,
        "bldgappr_generaldesc.area_2ndfloor": firstGenDesc.area2Ndfloor,
        "bldgappr_generaldesc.area_3rdfloor": firstGenDesc.area3Rdfloor,
        "bldgappr_generaldesc.area_4thfloor": firstGenDesc.area4Thfloor,
        "bldgappr_generaldesc.total_floor_area": firstGenDesc.totalFloorArea,
        "bldgappr_generaldesc.floor_sketch": null,
        "bldgappr_generaldesc.actual_use": firstGenDesc.actualUse,
        "bldgappr_generaldesc.unit_value": firstGenDesc.unitValue,
        "bldgappr_generaldesc.gen_code": "5TH",
        "bldgappr_struct_materials.date_created": "{{currentTimestamp}}",
        "bldgappr_struct_materials.date_modified": "{{currentTimestamp}}",
        "bldgappr_struct_materials.foundation": firstStructMat.foundation,
        "bldgappr_struct_materials.columns": firstStructMat.columns,
        "bldgappr_struct_materials.beams": firstStructMat.beams,
        "bldgappr_struct_materials.truss_framing": firstStructMat.trussFraming,
        "bldgappr_struct_materials.roof": firstStructMat.roof,
        "bldgappr_struct_materials.flooring": firstStructMat.flooring,
        "bldgappr_struct_materials.walls": firstStructMat.walls,
        "bldgappr_struct_materials.others": firstStructMat.others,
        "bldgappr_struct_materials.gen_code": "5TH",
        "bldgappr_property_assessment.date_created": "{{currentTimestamp}}",
        "bldgappr_property_assessment.date_modified": "{{currentTimestamp}}",
        "bldgappr_property_assessment.actual_use": firstAssess.actualUse,
        "bldgappr_property_assessment.market_value": firstAssess.marketValue,
        "bldgappr_property_assessment.assessment_level":
            firstAssess.assessmentLevel,
        "bldgappr_property_assessment.assessed_value":
            firstAssess.assessedValue,
        "bldgappr_property_assessment.taxable": firstAssess.taxable,
        "bldgappr_property_assessment.exempt": firstAssess.exempt,
        "bldgappr_property_assessment.qtr": firstAssess.qtr,
        "bldgappr_property_assessment.yr": firstAssess.yr,
        "bldgappr_property_assessment.appraisedby_name":
            firstAssess.appraisedbyName,
        "bldgappr_property_assessment.appraisedby_designation":
            firstAssess.appraisedbyDesignation,
        "bldgappr_property_assessment.appraisedby_date":
            DateFormat("yyyy-MM-dd")
                .format(DateTime.parse(firstAssess.appraisedbyDate!)),
        "bldgappr_property_assessment.recommendappr_name":
            firstAssess.recommendapprName,
        "bldgappr_property_assessment.recommendappr_designation":
            firstAssess.recommendapprDesignation,
        "bldgappr_property_assessment.recommendappr_date":
            DateFormat("yyyy-MM-dd")
                .format(DateTime.parse(firstAssess.recommendapprDate!)),
        "bldgappr_property_assessment.approvedby_name":
            firstAssess.approvedbyName,
        "bldgappr_property_assessment.approvedby_designation":
            firstAssess.approvedbyDesignation,
        "bldgappr_property_assessment.approvedby_date": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstAssess.approvedbyDate!)),
        "bldgappr_property_assessment.memoranda": firstAssess.memoranda,
        "bldgappr_property_assessment.note": firstAssess.note,
        "bldgappr_property_assessment.swornstatement_no":
            firstAssess.swornstatementNo,
        "bldgappr_property_assessment.date_received": DateFormat("yyyy-MM-dd")
            .format(DateTime.parse(firstAssess.dateReceived!)),
        "bldgappr_property_assessment.entry_date_assessment":
            DateFormat("yyyy-MM-dd")
                .format(DateTime.parse(firstAssess.entryDateAssessment!)),
        "bldgappr_property_assessment.entry_date_by": " ",
        "bldgappr_property_assessment.gen_code": "5TH",
        "bldgappr_rec_supersededass.date_created": "{{currentTimestamp}}",
        "bldgappr_rec_supersededass.date_modified": "{{currentTimestamp}}",
        "bldgappr_rec_supersededass.pin": " ",
        "bldgappr_rec_supersededass.tdn": " ",
        "bldgappr_rec_supersededass.total_assval": "0",
        "bldgappr_rec_supersededass.owner": " ",
        "bldgappr_rec_supersededass.effectivity_ass": null,
        "bldgappr_rec_supersededass.page_no": "0",
        "bldgappr_rec_supersededass.total_marketval": "0",
        "bldgappr_rec_supersededass.total_area": "0",
        "bldgappr_rec_supersededass.rec_assessment": null,
        "bldgappr_rec_supersededass.rec_taxmapping": " ",
        "bldgappr_rec_supersededass.rec_records": " ",
        "bldgappr_rec_supersededass.date": null,
        "bldgappr_rec_supersededass.gen_code": "5TH"
      };

      return details;
    }

    Future<void> _postAdditionalItems(
        Map<String, dynamic> datas, PropertyInfo infos) async {
      String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z";
      String xClientKeySecret = "unitcYqAN7GGalyz";
      Map<String, String> headers = {
        'Content-Type': 'application/json; charset=UTF-8',
        'X-Client-Key': xClientKey,
        'X-Client-Secret': xClientKeySecret
      };

      List<Map<String, dynamic>> addItems =
          await SQLServices.instance.getAdditionalItems(infos.id);

      List<AdditionalItems> addItemsList =
          addItems.map((map) => AdditionalItems.fromJson(map)).toList();

      for (AdditionalItems items in addItemsList) {
        // Populate AdditionalItems model here
        final addItems = AdditionalItems(
            id: 1,
            bldgapprDetailsId: datas['data']['id'],
            classId: items.classId,
            className: items.className,
            structType: items.structType,
            unitValue: items.unitValue,
            baseUnitValue: items.baseUnitValue,
            area: items.area,
            marketValue: items.marketValue,
            depreciationRate: items.depreciationRate,
            adjustedMarketVal: items.adjustedMarketVal,
            amtDepreciation: items.amtDepreciation,
            painted: items.painted,
            secondhand: items.secondhand,
            paintedUnitval: items.paintedUnitval,
            secondhandUnitval: items.secondhandUnitval,
            actualUse: items.actualUse,
            genCode: "5TH");
        Response addResponse = await post(
            Uri.parse(
                'https://${Url.instance.host()}/api/rptass_app/additional_items/'),
            headers: headers,
            body: jsonEncode(addItems));
        print(addResponse.body);
      }
    }

    Future<void> _postBuildingStructures(
        Map<String, dynamic> datas, PropertyInfo infos) async {
      String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z";
      String xClientKeySecret = "unitcYqAN7GGalyz";
      Map<String, String> headers = {
        'Content-Type': 'application/json; charset=UTF-8',
        'X-Client-Key': xClientKey,
        'X-Client-Secret': xClientKeySecret
      };

      List<Map<String, dynamic>> bldgStructures =
          await SQLServices.instance.getBuildingAndStructure(infos.id);

      List<BldgAndStructure> bldgStructureList =
          bldgStructures.map((map) => BldgAndStructure.fromJson(map)).toList();
      for (BldgAndStructure structure in bldgStructureList) {
        final bldgStruc = BldgAndStructure(
            id: 1,
            bldgapprDetailsId: datas['data']['id'],
            bldgArea: structure.bldgArea,
            bldgType: structure.bldgType,
            structType: structure.structType,
            description: structure.description,
            actualUse: structure.actualUse,
            floorCount: structure.floorCount,
            unitValue: structure.unitValue,
            depRate: structure.depRate,
            marketValue: structure.marketValue,
            depAmount: structure.depAmount,
            adjustedMarketValue: structure.adjustedMarketValue,
            genCode: '5TH',
            buccPercentage: structure.buccPercentage);
        Response response = await post(
            Uri.parse(
                'https://${Url.instance.host()}/api/rptass_app/bldgappr_structure/'),
            headers: headers,
            body: jsonEncode(bldgStruc));
        print(response.body);
      }
    }

    Future<Response> _postBuildingDetails(Map<String, dynamic> details) async {
      String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z";
      String xClientKeySecret = "unitcYqAN7GGalyz";
      Map<String, String> headers = {
        'Content-Type': 'application/json; charset=UTF-8',
        'X-Client-Key': xClientKey,
        'X-Client-Secret': xClientKeySecret
      };

      return await post(
          Uri.parse(
              'https://${Url.instance.host()}/api/rptass_app/bldgappr_details/'),
          headers: headers,
          body: jsonEncode(details));
    }

    Future<Response> _postFloorSketch(
        Map<String, dynamic> details, file) async {
      String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z";
      String xClientKeySecret = "unitcYqAN7GGalyz";

      // Construct the headers for the request
      Map<String, String> headers = {
        'Content-Type': 'multipart/form-data',
        'X-Client-Key': xClientKey,
        'X-Client-Secret': xClientKeySecret,
      };

      // Create a MultipartRequest
      var request = MultipartRequest(
        'POST',
        Uri.parse(
            'https://${Url.instance.host()}/api/rptass_app/bldgappr_sketch/'),
      );

      // Add the headers to the request
      request.headers.addAll(headers);

      // Add JSON data as a field
      // Add individual fields to the request
      details.forEach((key, value) {
        request.fields[key] = value.toString();
      });

      // Add the floor sketch image file, if it exists

      var fileName = basename(file);
      request.files.add(
        await MultipartFile.fromPath(
          'floor_sketch', // Field name in the API
          file,
          filename: fileName,
        ),
      );

      // Send the request and get the response
      var streamedResponse = await request.send();
      return await Response.fromStream(streamedResponse);
    }

    Future<void> _uploadImage(data, infos) async {
      // Create a map with the required fields

      List<Map<String, dynamic>> floorSketch =
          await SQLServices.instance.getFloorSketch(infos.id);

      // Parse data
      FloorSketch firstFs = FloorSketch.fromJson(floorSketch.first);
      var file = File(firstFs.floorSketch!);
      Map<String, dynamic> detailsMap = {
        "bldgappr_details_id": data['data']['id'], // int8 NOT NULL
        "date_created": DateTime.now().toIso8601String(), // timestamptz NULL
        "floor_sketch": firstFs.floorSketch!, // text NULL
        "gen_code": "5TH", // varchar(20) NOT NULL
      };

      try {
        Response response = await _postFloorSketch(detailsMap, file.path);
        print(response.body);

        if (response.statusCode == 201) {
          print('Upload successful');
        } else {
          print('Upload failed with status: ${response.statusCode}');
        }
      } catch (e) {
        print('Error: $e');
      }
    }

    on<UploadBuildingFaas>((event, emit) async {
      emit(UploadBuildingFaasLoading());
      try {
        List<PropertyInfo> propertyOwner =
            await SQLServices.instance.readAllBldgOwner();

        for (PropertyInfo infos in propertyOwner) {
          if (infos.dateSynced == null) {
            final details = await _prepareBuildingDetails(event, infos);

            Response detailsResponse = await _postBuildingDetails(details);
            final datas = json.decode(detailsResponse.body);

            print(datas);

            await _postAdditionalItems(datas, infos);
            await _postBuildingStructures(datas, infos);
            await _uploadImage(datas, infos);

            if (detailsResponse.statusCode == 201) {
              final detailsInfo = PropertyInfo(
                  id: infos.id,
                  transCode: infos.transCode,
                  assessedById: infos.assessedById,
                  assessedByName: infos.assessedByName,
                  tdn: infos.tdn,
                  pin: infos.pin,
                  fname: infos.fname,
                  mname: infos.mname,
                  bday: infos.bday,
                  lname: infos.lname,
                  address: infos.address,
                  telno: infos.telno,
                  tin: infos.tin,
                  adminUser: infos.adminUser,
                  adminAddress: infos.adminAddress,
                  adminTin: infos.adminTin,
                  adminTelno: infos.adminTelno,
                  faasType: "Building",
                  dateSynced:
                      DateFormat('MM/dd/yyyy hh:mm a').format(DateTime.now()));

              await SQLServices.instance.updateBldgOwner(infos.id, detailsInfo);
            }
          }

          propertyOwner = await SQLServices.instance.readAllBldgOwner();
          emit(PropertyInfoLoaded(propertyInfos: propertyOwner));
        }
      } catch (e) {
        print(e.toString());
        emit(PropertyOwnerInfoErrorState(errorMessage: e.toString()));
      }
      ;
    });
  }
}
