package cz.muni.pa165.oauth2.client;

import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import cz.muni.fi.pa165.model.dto.user.UserCreateDto;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.apache.http.entity.StringEntity;

import java.io.IOException;
import java.net.http.HttpClient;
import java.text.ParseException;
import java.util.Map;

import static org.hibernate.validator.internal.util.Contracts.assertTrue;

/**
 * Spring MVC Controller.
 * Handles HTTP requests by preparing data in model and passing it to Thymeleaf HTML templates.
 */
@Controller
public class MainController {

    private static final Logger log = LoggerFactory.getLogger(MainController.class);

    /**
     * Home page accessible even to non-authenticated users. Displays user personal data.
     */
    @GetMapping("/")
    public String index(Model model, @AuthenticationPrincipal OidcUser user) {
        log.debug("********************************************************");
        log.debug("* index() called                                       *");
        log.debug("********************************************************");
        log.debug("user {}", user == null ? "is anonymous" : user.getSubject());
        log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue());

        // put obtained user data into a model attribute named "user"
        model.addAttribute("user", user);

        // put issuer name into a model attribute named "issuerName"
        if (user != null) {
            model.addAttribute("issuerName",
                    "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google");

            model.addAttribute("token", user.getIdToken().getTokenValue());
        }

        // return the name of a Thymeleaf HTML template that
        // will be searched in src/main/resources/templates with .html suffix
        return "index";
    }

    /**
     * Home page accessible even to non-authenticated users. Displays user personal data.
     */
    @GetMapping("/register")
    public String register(Model model, @AuthenticationPrincipal OidcUser user) {
        log.debug("********************************************************");
        log.debug("* register() called                                       *");
        log.debug("********************************************************");
        log.debug("user {}", user == null ? "is anonymous" : user.getSubject());
        log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue());

        // put obtained user data into a model attribute named "user"
        model.addAttribute("user", user);

        // put issuer name into a model attribute named "issuerName"
        if (user != null) {
            if (!Registrate(user.getIdToken().getTokenValue())){
                return "error";
            };

            model.addAttribute("issuerName",
                    "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google");

            model.addAttribute("token", user.getIdToken().getTokenValue());
        }

        // return the name of a Thymeleaf HTML template that
        // will be searched in src/main/resources/templates with .html suffix
        return "index";
    }

    private Boolean Registrate(String token) {
        String payload = CreatePayload(token);
        StringEntity entity = new StringEntity(payload,
                ContentType.APPLICATION_JSON);

        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost request = new HttpPost("http://localhost:8080/api/user");
        request.setEntity(entity);
        request.setHeader("Content-Type", "application/json");
        request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        HttpResponse response = null;
        try{
            response = httpClient.execute(request);
            // assert 201
        }
        catch (Exception e){
            return false;
        }

        return true;
    }

    private String CreatePayload(String token){
        UserCreateDto userCreateDto = GetUserCreateDto(token);

        String payload = String.format("""
                {
                    "username": "%s",
                    "email": "%s",
                    "firstName": "%s",
                    "lastName": "%s"
                }
                """, userCreateDto.getUsername(),
                userCreateDto.getEmail(),
                userCreateDto.getFirstName(),
                userCreateDto.getLastName());

        return payload;
    }

    private UserCreateDto GetUserCreateDto(String token){

        JWSObject jwsObject = null;
        try {
            jwsObject = JWSObject.parse(token);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        Payload payload = jwsObject.getPayload();
        Map<String, Object> jsonObject = payload.toJSONObject();

        String email = (String) jsonObject.get("email");
        String userName = (String) jsonObject.get("name"); // or preferred_username
        String firstName = (String) jsonObject.get("given_name");
        String lastName = (String) jsonObject.get("family_name");

        UserCreateDto userCreateDto = new UserCreateDto();
        userCreateDto.setEmail(email);
        userCreateDto.setUsername(userName);
        userCreateDto.setFirstName(firstName);
        userCreateDto.setLastName(lastName);

        return userCreateDto;
    }

}