package cz.utb.tadis.map.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import cz.utb.tadis.auth.model.User;
import cz.utb.tadis.common.http.ApiResponse;
import cz.utb.tadis.map.model.*;
import cz.utb.tadis.map.repository.MapAttacksRepository;
import cz.utb.tadis.map.repository.MapSettingsRepository;
import cz.utb.tadis.security.SecurityContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;

@PreAuthorize("hasAnyRole('USER', 'ANALYST', 'ADMIN')")
@RestController
@RequestMapping("/map")
public class MapController {

    private final String MAPBOX_USER;
    private final String MAPBOX_TOKEN;

    @Autowired
    SecurityContextUtil securityContextUtil;

    @Autowired
    MapAttacksRepository mapAttacksRepository;

    @Autowired
    MapSettingsRepository mapSettingsRepository;

    public MapController(@Value("${cz.utb.tadis.map.mapboxUser}") String mapboxUser, @Value("${cz.utb.tadis.map.mapboxToken}") String mapboxToken) {
        MAPBOX_USER = mapboxUser;
        MAPBOX_TOKEN = mapboxToken;
    }

    @GetMapping
    public ResponseEntity getMapData(){
        User loggedUser = securityContextUtil.getUserFromContext();

        List<MapAttack> attacks = mapAttacksRepository.findAll();
        if ("ROLE_USER".equals(loggedUser.getRole().getName())){
            for(MapAttack attack : attacks){
                attack.setId(null);
            }
        }

        MapSettings settings = mapSettingsRepository.findFirstByOrderByIdDesc();

        return new ResponseEntity(new MapData(attacks, settings), HttpStatus.OK);
    }

    @PutMapping("/settings")
    public ResponseEntity saveMapSettings(@Valid @RequestBody MapSettings mapSettings){
        MapSettings savedSettings = this.mapSettingsRepository.save(mapSettings);
        return new ResponseEntity(savedSettings, HttpStatus.OK);
    }

    @GetMapping("/token")
    public ResponseEntity getMapboxToken(){
        String mapboxEndpoint = "https://api.mapbox.com/tokens/v2/" + MAPBOX_USER + "?access_token=" + MAPBOX_TOKEN;
        String expires = Instant.now().plusSeconds(60 * 15).truncatedTo(ChronoUnit.SECONDS).toString();
        TemporaryTokenRequest requestBody = new TemporaryTokenRequest(expires, new String[] {"styles:tiles", "styles:read", "fonts:read", "datasets:read", "vision:read"});

        TemporaryTokenResponse temporaryTokenResponse = null;
        try{
            temporaryTokenResponse = requestToken(mapboxEndpoint, requestBody);
        }
        catch(IOException e){
            System.out.println(e);
            return new ResponseEntity(new ApiResponse(HttpStatus.SERVICE_UNAVAILABLE, "Mapbox API Error", "Couldn't retrieve temporary token from mapbox API"), HttpStatus.SERVICE_UNAVAILABLE);
        }

        return new ResponseEntity(temporaryTokenResponse, HttpStatus.OK);
    }

    private TemporaryTokenResponse requestToken(String endPoint, TemporaryTokenRequest body) throws IOException {
        URL url = new URL(endPoint);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setRequestProperty("Accept","application/json");
        con.setRequestProperty("Content-Type","application/json");
        con.setDoOutput(true);

        OutputStream os = con.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
        ObjectMapper objectMapper = new ObjectMapper();

        osw.write(objectMapper.writeValueAsString(body));
        osw.flush();
        osw.close();
        os.close();

        con.connect();

        String result;
        BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        int subresult = bis.read();
        while(subresult != -1) {
            buf.write((byte) subresult);
            subresult = bis.read();
        }
        result = buf.toString();
        TemporaryTokenResponse response = objectMapper.readValue(result, TemporaryTokenResponse.class);
        return response;
    }

}
