package com.jakubu9333.grammarManager.api;

import com.fasterxml.jackson.annotation.JsonView;
import com.jakubu9333.grammarManager.model.FileMetadata;
import com.jakubu9333.grammarManager.model.Grammar;
import com.jakubu9333.grammarManager.model.View;
import com.jakubu9333.grammarManager.service.FileService;
import org.javatuples.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;

import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author Jakub Uhlarik
 */

@RestController
@CrossOrigin(origins = "http://localhost:3000")
@RequestMapping(path = "api/files")
public class FilesControler {
    private final FileService fileService;

    @Autowired
    public FilesControler(FileService fileService) {
        this.fileService = fileService;
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteFile(@PathVariable Long id) {
        fileService.deleteFileById(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @DeleteMapping("/grammar/{id}")
    public ResponseEntity<?> deleteFileFromGrammar(@PathVariable Long id) {
        fileService.deleteFilesFromGrammar(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @JsonView(View.All.class)
    @GetMapping("/grammar/{id}")
    public ResponseEntity<List<FileMetadata>> fileHoldersFromGrammar(@PathVariable Long id) {
        return new ResponseEntity<>(fileService.getFilesFromGrammar(id), HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity<String> getFile(@PathVariable Long id, HttpServletResponse response, @RequestParam(defaultValue = "false") boolean download) {
        MediaType typeOfFile;
        Pair<FileMetadata, String> result = fileService.getFileHolderAndStringById(id);
        FileMetadata fileMeta = result.getValue0();
        String fileName;
        if (fileMeta.isXml()) {
            fileName = "grammar" + id + ".grxml";
            typeOfFile = MediaType.APPLICATION_XML;
        } else {
            fileName = "grammar" + id + ".jsgf";
            typeOfFile = MediaType.TEXT_PLAIN;
        }
        if (download) {
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        }
        return ResponseEntity.ok().contentType(typeOfFile).body(result.getValue1());
    }


    @GetMapping("grammar/{grammarId}/generated")
    public ResponseEntity<String> fileFromGrammar(@RequestParam String format, @PathVariable Long grammarId, @RequestParam(defaultValue = "false") boolean download, HttpServletResponse response) {
        MediaType typeOfFile;
        boolean xml;
        String fileName;
        switch (format) {
            case "jsgf" -> {
                typeOfFile = MediaType.TEXT_PLAIN;
                xml = false;
                fileName = grammarId + "_generated_g.jsgf";
            }
            case "xml" -> {
                typeOfFile = MediaType.APPLICATION_XML;
                xml = true;
                fileName = grammarId + "_generated_g.grxml";
            }
            default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid file format");
        }
        Pair<String, Integer> result = fileService.newFileFromGrammar(grammarId, xml, false);
        if (download) {
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        }
        return ResponseEntity.status( HttpStatus.OK).contentType(typeOfFile).body(result.getValue0());
    }

    @PutMapping("grammar/{grammarId}/generated")
    public ResponseEntity<String> generateFileFromGrammar(@RequestParam String format, @PathVariable Long grammarId, @RequestParam(defaultValue = "false") boolean download, HttpServletResponse response) {
        MediaType typeOfFile;
        boolean xml;
        switch (format) {
            case "jsgf" -> {
                typeOfFile = MediaType.TEXT_PLAIN;
                xml = false;
            }
            case "xml" -> {
                typeOfFile = MediaType.APPLICATION_XML;
                xml = true;
            }
            default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid file format");
        }
        Pair<String, Integer> result = fileService.newFileFromGrammar(grammarId, xml, true);
        return ResponseEntity.status( result.getValue1()==1? HttpStatus.CREATED :HttpStatus.OK).contentType(typeOfFile).body(result.getValue0());
    }

    @JsonView(View.RuleWithoutGrammar.class)
    @PostMapping("")
    public ResponseEntity<Grammar> grammarFromFile(@RequestBody String file, @RequestParam(defaultValue = "true") boolean save, @RequestParam(required = false) String format) {
        if (format == null) {
            Grammar grammar = fileService.addGrammarFromFile(file, null, null, false, save).getValue0();
            return new ResponseEntity<>(grammar, HttpStatus.CREATED);
        }
        Boolean xml;
        switch (format) {
            case "jsgf" -> xml = false;

            case "xml" -> xml = true;
            case "u" -> xml = null;
            default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid file format");
        }
        Grammar grammar = fileService.addGrammarFromFile(file, xml, null, false, save).getValue0();
        return new ResponseEntity<>(grammar, HttpStatus.CREATED);
    }

    @JsonView(View.RuleWithoutGrammar.class)
    @PostMapping("{id}")
    public ResponseEntity<Grammar> grammarFromFile(@RequestBody String file, @RequestParam(required = false) String format, @PathVariable Long id, @RequestParam(defaultValue = "false") boolean create, @RequestParam(defaultValue = "true") boolean save) {
        Boolean xml;
        if (format == null) {
            format = "u";
        }
        switch (format) {
            case "jsgf" -> xml = false;

            case "xml" -> xml = true;
            case "u" -> xml = null;
            default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid file format");
        }
        Pair<Grammar, Boolean> result = fileService.addGrammarFromFile(file, xml, id, create, save);
        if (result == null) {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return new ResponseEntity<>(result.getValue0(), result.getValue1() ? HttpStatus.CREATED : HttpStatus.OK);
    }
}
