package io.gmss.fiscad.implementations.infocad.metier;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gmss.fiscad.entities.infocad.metier.Enquete;
import io.gmss.fiscad.entities.infocad.metier.Parcelle;
import io.gmss.fiscad.entities.infocad.metier.ParcelleGeom;
import io.gmss.fiscad.entities.infocad.metier.Upload;
import io.gmss.fiscad.enums.StatutParcelle;
import io.gmss.fiscad.exceptions.BadRequestException;
import io.gmss.fiscad.exceptions.NotFoundException;
import io.gmss.fiscad.interfaces.infocad.metier.ParcelleGeomService;
import io.gmss.fiscad.interfaces.synchronisation.SynchronisationService;
import io.gmss.fiscad.paylaods.request.UploadPayLoad;
import io.gmss.fiscad.paylaods.response.EnqueteCheckResponse;
import io.gmss.fiscad.repositories.infocad.metier.EnqueteRepository;
import io.gmss.fiscad.repositories.infocad.metier.ParcelleGeomRepository;
import io.gmss.fiscad.repositories.infocad.metier.UploadRepository;
import io.gmss.fiscad.service.FileStorageService;
import lombok.RequiredArgsConstructor;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class ParcelleGeomServiceImpl implements ParcelleGeomService {

    private final ParcelleGeomRepository parcelleGeomRepository;
    private final EnqueteRepository enqueteRepository;
    private final ObjectMapper objectMapper;
    private final FileStorageService fileStorageService;
    private final UploadRepository uploadRepository;

    @Override
    public int createParcelleFromGeoJsonFile(MultipartFile file,String reference,String description) throws BadRequestException {
        InputStream inputStream = null;
        JsonNode rootNode=null;
        try {
            inputStream = file.getInputStream();
            rootNode = objectMapper.readTree(inputStream);
        } catch (IOException e) {
            throw new RuntimeException("Le fichier ne respecte pas le format d'un GeoJson");
        }
        int n=0;
        if (!rootNode.has("features")) {
            throw new  BadRequestException("Le fichier ne respecte pas le format d'un GeoJson");
        }

        WKTReader wktReader = new WKTReader();
        Iterator<JsonNode> features = rootNode.get("features").elements();

        Upload upload= saveGeoJsonUpload(file,reference,description);
        while (features.hasNext()) {
            n++;
            JsonNode feature = features.next();
            createOnParcelleFromGeoJson(feature,upload);
        }
        //////Creation de la geometry
        try {
            parcelleGeomRepository.creationGeometry();
            parcelleGeomRepository.majCentroidParcelle();
            uploadRepository.majNombreParcelle(n,upload.getId());//  save(upload);
        }catch (Exception e){
        }

        try {
            parcelleGeomRepository.majIdQuartier();
        }catch (Exception e){
        }

        return n;
    }

    private Upload saveGeoJsonUpload(MultipartFile file,String reference,String desc) {
        Upload upload = new Upload();
        String fileName = fileStorageService.storeFile(file);
        upload.setReference(reference);
        upload.setDescription(desc);
        upload.setFileName(fileName);
        upload.setMimeType(file.getContentType());
        upload.setSize(file.getSize());
        upload.setOriginalFileName(file.getOriginalFilename());
        uploadRepository.save(upload);
        return upload;
    }

    @Override
    public ParcelleGeom createOnParcelleFromGeoJson(JsonNode feature,Upload upload) throws BadRequestException {
        JsonNode geometryNode = feature.get("geometry");
        JsonNode propertiesNode = feature.get("properties");
        WKTReader wktReader = new WKTReader();
        if (geometryNode != null) {

            Polygon geometry = null;
            ParcelleGeom parcelleGeom = new ParcelleGeom();
            try {
                geometry = convertGeoJsonToPolygon(geometryNode.toString());
                String   polygoneString =geometry.getExteriorRing().toString();
                polygoneString=polygoneString.replace("LINEARRING (","POLYGON ((" );
                polygoneString=polygoneString.replace(")","))" );
                parcelleGeom.setGeometryString(polygoneString);
            } catch (ParseException e) {
                e.printStackTrace();
                //throw new RuntimeException(e);
            } catch (Exception e) {
                e.printStackTrace();
                // throw new RuntimeException(e);
            }
            parcelleGeom.setUploadId(upload.getId());

            parcelleGeom.setDepartement(propertiesNode.get("DEPARTEMENT").textValue());
            parcelleGeom.setCommune(propertiesNode.get("COMMUNE").textValue());
            parcelleGeom.setArrondissement(propertiesNode.get("ARRONDISSEMENT").textValue());
            parcelleGeom.setVillageQuartier(propertiesNode.get("VILLAGE_QUARTIER").textValue());
            parcelleGeom.setCodeBloc(propertiesNode.get("CODE_BLOC").textValue());
            parcelleGeom.setNupProvisoire(propertiesNode.get("CODE_PARCELLE").textValue());
            parcelleGeom.setCodeEquipe(propertiesNode.get("CODE_EQUIPE").textValue());
            try {
                parcelleGeom.setSuperficie(Integer.parseInt(propertiesNode.get("SUPERFICIE").textValue()));
            }catch (Exception e){
            }
            parcelleGeom.setElNumeroEtatLieux(propertiesNode.get("EL_NUMERO_ETAT_LIEUX").textValue());
            parcelleGeom.setElLot(propertiesNode.get("EL_LOT").textValue());
            parcelleGeom.setRfuQuartierOuZone(propertiesNode.get("RFU_QUARTIER_OU_ZONE").textValue());
            parcelleGeom.setElLettreParcelle(propertiesNode.get("EL_LETTRE_PARCELLE").textValue());
            parcelleGeom.setRfuIlot(propertiesNode.get("RFU_ILOT").textValue());
            parcelleGeom.setRfuLettreParcelle(propertiesNode.get("RFU_LETTRE_PARCELLE").textValue());
            parcelleGeom.setDateCollecte(propertiesNode.get("DATE_COLLECTE").textValue());
            parcelleGeom.setNomLotissement(propertiesNode.get("NOM_LOTISSEMENT").textValue());
            parcelleGeom.setNomEtPrenoms(propertiesNode.get("NOM_ET_PRENOMS").textValue());
            parcelleGeom.setSourceCollecte(propertiesNode.get("SOURCE_COLLECTE").textValue());
            parcelleGeom.setObservations(propertiesNode.get("OBSERVATIONS").textValue());
            parcelleGeom= parcelleGeomRepository.save(parcelleGeom);
            try {
                setStatutParcelleGeomFromGeoJson(parcelleGeom);
            }catch (Exception e){
                e.printStackTrace();
            }

            return parcelleGeom;
        }else {
            return null;
        }
    }

    @Override
    public ParcelleGeom updateParcelleGeom(MultipartFile file) throws NotFoundException {
        return null;
    }

    @Override
    public void deleteParcelle(Long id) throws NotFoundException {

    }

    @Override
    public Page<ParcelleGeom> getParcelleGeomList(Pageable pageable) {
        return parcelleGeomRepository.findAll(pageable);
    }

    @Override
    public List<ParcelleGeom> getParcelleGeomList() {
        return parcelleGeomRepository.findAll();
    }

    @Override
    public List<ParcelleGeom> getParcelleGeomListUnQuatier(String codeQuartier) {
        return parcelleGeomRepository.findAllByQuartier_Code(codeQuartier);
    }



    @Override
    public Optional<ParcelleGeom> getParcelleGeomById(Long id) {
        return parcelleGeomRepository.findById(id);
    }

    @Override
    public void setStatutParcelleGeomFromGeoJson(ParcelleGeom parcelleGeom) {
        if(parcelleGeom.getNupProvisoire()!=null) {
            List<EnqueteCheckResponse> enqueteCheckResponses = enqueteRepository.getEnquetByNupProvisoir(parcelleGeom.getNupProvisoire());

            if (enqueteCheckResponses.isEmpty()) {
                System.out.println("NON ENQUETE");
                parcelleGeomRepository.majStatutParcelleGeomNonEnqueter(parcelleGeom.getNupProvisoire());
                //parcelleGeom.setStatutParcelle(StatutParcelle.NON_ENQUETER);
            } else {
                if (enqueteCheckResponses.get(0).getNombreBatiment() != null && enqueteCheckResponses.get(0).getNombreBatiment() != 0) {
                    // parcelleGeom.setStatutParcelle(StatutParcelle.ENQUETER_BATIE);
                    parcelleGeomRepository.majStatutParcelleGeomBatie(parcelleGeom.getNupProvisoire());
                } else {
                    parcelleGeomRepository.majStatutParcelleGeomNonBatie(parcelleGeom.getNupProvisoire());
                }
            }
            enqueteCheckResponses.clear();
//        parcelleGeomRepository.save(parcelleGeom);
        }else{
            parcelleGeomRepository.majStatutParcelleGeomNonEnqueter(parcelleGeom.getNupProvisoire());
        }

    }

    @Override
    public void setStatutParcelleGeomFromEnquete(Enquete enquete) {

                if(enquete.getNbreBatiment()!=0) {
                    parcelleGeomRepository.majStatutParcelleGeomBatie(enquete.getCodeParcelle());
                }else{
                    parcelleGeomRepository.majStatutParcelleGeomNonBatie(enquete.getCodeParcelle());
                }


    }

    @Override
    public void linkParcelleToParcelleGeom(String nupProvisoir,Long parcelleId) {
//        Optional<ParcelleGeom> parcelleGeomOptional=parcelleGeomRepository.findFirstByNupProvisoire(nupProvisoir);
//        if(parcelleGeomOptional.isPresent()){
//            parcelleGeomOptional.get().par
//        }
    }


    public  Polygon convertGeoJsonToPolygon(String geoJson) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(geoJson);

        if (!rootNode.has("type") || !rootNode.has("coordinates")) {
            throw new IllegalArgumentException("GeoJSON invalide");
        }

        String type = rootNode.get("type").asText();
        JsonNode coordinatesNode = rootNode.get("coordinates");

        GeometryFactory geometryFactory = new GeometryFactory();

        if ("Polygon".equals(type) || "geometry".equals(type)) {
            JsonNode firstRing = coordinatesNode.get(0);
            Coordinate[] coordinates = new Coordinate[firstRing.size()];
            for (int i = 0; i < firstRing.size(); i++) {
                JsonNode point = firstRing.get(i);
                double x = point.get(0).asDouble();
                double y = point.get(1).asDouble();
                coordinates[i] = new Coordinate(x, y);
            }
            return geometryFactory.createPolygon(coordinates);
        }
        throw new UnsupportedOperationException("Type GeoJSON non supporté: " + type);
    }


}
