package com.pack.modeler.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.pack.modeler.assembler.ArtifactAssembler;
import com.pack.modeler.assembler.BPMNAssambler;
import com.pack.modeler.assembler.ErrorAssembler;
import com.pack.modeler.assembler.LogRowAssembler;
import com.pack.modeler.filter.ArtifactFilter;
import com.pack.modeler.manager.FileStorageManager;
import com.pack.modeler.model.ActivityData;
import com.pack.modeler.model.BPMNData;
import com.pack.modeler.model.DiagramData;
import com.pack.modeler.service.*;
import com.pack.modeler.structures.LogRow;
import com.pack.modeler.structures.MethodParameters;
import com.pack.modeler.structures.StatisticalMeasures;
import com.pack.modeler.structures.UserError;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


@RestController
@RequestMapping("/")
@CrossOrigin
public class Controller {

    Type artifactItemsMapType = new TypeToken<ArrayList<Map<String, ArtifactFilter>>>() {}.getType();

    @Autowired
    private LogRowAssembler logRowAssembler;

    @Autowired
    private ErrorAssembler errorAssembler;

    @Autowired
    private ArtifactAssembler artifactAssembler;

    @Autowired
    private BPMNAssambler bpmnAssambler;


    @Autowired
    private ParsingService fileParser = new ParsingService();

    @Autowired
    private UploadService uploadService = new UploadService();

    @Autowired
    private ConverterService converterService = new ConverterService();

    @Autowired
    private DiscoveryService discoveryService = new DiscoveryService();

    @Autowired
    private BPMNFileStorageService bpmnFileStorageService = new BPMNFileStorageService();

    @Autowired
    private ModelService modelService = new ModelService();

    @PostMapping("/upload")
    public Resources getKey(@RequestParam("file") MultipartFile file){
        String extension = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase();

        if(extension.equals("csv") || extension.equals("xes")) {
            String token = uploadService.saveFile(file);

            List<String> response = new ArrayList<>();
            response.add(token);
            return new Resources<>(response);
        }
        else
        {
            List<UserError> errorList = new ArrayList<>();
            errorList.add(UserError.WRONG_FILE_FORMAT);
            return errorAssembler.toResources(errorList);
        }

    }

    @PostMapping("/parse/{token}")
    public Resources getRows(@PathVariable("token") String token, @RequestParam("sep") String sep) {
        List<LogRow> rows = fileParser.parseRows(token, sep, 10);
        if (rows != null)
            return logRowAssembler.toResources(rows);
        else {
            List<UserError> errorList = new ArrayList<>();
            errorList.add(UserError.WRONG_TOKEN);
            return errorAssembler.toResources(errorList);
        }
    }

    @GetMapping("/artifacts/{token}")
    public Resources getArtifacts(@PathVariable("token") String token) {
        Map<String, List<String>> artifacts = fileParser.getActivities(token);

        return artifactAssembler.toResources(artifacts);
    }

    @PostMapping("/update/{token}")
    public Resources updateCSV(@PathVariable("token") String token, @RequestParam("map") String jsonMap, @RequestParam String dateformat) {
        Gson gson = new GsonBuilder().disableHtmlEscaping().setDateFormat(dateformat).create();
        Map<String, String> map = gson.fromJson(jsonMap, (Class<Map<String,String>>)(Class)Map.class);

        converterService.convertCSVtoXES(token, map, dateformat);
        return null;
    }

    @PostMapping("/discovery/{token}")
    public ResponseEntity startFrequencyProcessing(@PathVariable("token") String token,
                                                   @RequestParam("discovery") String discovery,
                                                   @RequestParam("algorithm") String algorithm,
                                                   @RequestParam("statMeasures") String statisticalMeasures,
                                                   @RequestParam("methodParams") String methodParameters,
                                                   @RequestParam("artifactFilterMap") String artifactMap
                                     ) throws InterruptedException, ExecutionException {
        Gson gson = new GsonBuilder().disableHtmlEscaping().create();
        StatisticalMeasures statMeasures = gson.fromJson(statisticalMeasures, StatisticalMeasures.class);
        MethodParameters methodParams = gson.fromJson(methodParameters, MethodParameters.class);
        List<Map<String, ArtifactFilter>> artifactFilterMap = gson.fromJson(artifactMap, artifactItemsMapType);

        FileStorageManager.getInstance().setLinkPath();
        CompletableFuture<Boolean> processResult = discoveryService.discover(token, discovery, algorithm, statMeasures, methodParams, artifactFilterMap);
        CompletableFuture.allOf(processResult).join();

        if(processResult.get() == true)
            return new ResponseEntity(HttpStatus.OK);
        return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @GetMapping("/readFile/{fileId}")
    public ResponseEntity<ByteArrayResource> downloadFile(@PathVariable String fileId) {
        // Load file from database
        BPMNData file = bpmnFileStorageService.getFile(fileId);

        Path path = Paths.get(file.getFile());

        byte[] bytes = new byte[0];
        try {
            bytes = Files.readAllBytes(path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return ResponseEntity.ok()
                .contentType(MediaType.TEXT_XML)
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + path.getFileName().toString() + "\"")
                .body(new ByteArrayResource(bytes));
    }

    @GetMapping("/result/{token}")
    public Resources fetchResult(@PathVariable("token") String token) {
        List<DiagramData> bpmns = modelService.fetchResult(token);
        return bpmnAssambler.toResources(bpmns);
    }

    @GetMapping("/activityStats/{token}")
    public Resources activityStats(@PathVariable("token") String token) {
        List<ActivityData> actData = modelService.fetchActStats(token);
        return bpmnAssambler.toResourcesActData(actData);
    }
}