package oauth

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"gitea.local/admin/hspguard/internal/types"
	"gitea.local/admin/hspguard/internal/util"
	"gitea.local/admin/hspguard/internal/web"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
	log.Println("[OAUTH] New request to token endpoint")

	authHeader := r.Header.Get("Authorization")
	if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") {
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	// Decode credentials
	payload, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
	if err != nil {
		http.Error(w, "Invalid auth encoding", http.StatusBadRequest)
		return
	}

	var clientId string
	var clientSecret string

	parts := strings.SplitN(string(payload), ":", 2)
	if len(parts) != 2 {
		http.Error(w, "Unauthorized", http.StatusForbidden)
		return
	}

	clientId = parts[0]
	clientSecret = parts[1]

	log.Printf("Some client is trying to exchange code with id: %s and secret: %s\n", clientId, clientSecret)

	// Parse the form data
	err = r.ParseForm()
	if err != nil {
		http.Error(w, "Failed to parse form", http.StatusBadRequest)
		return
	}

	grantType := r.FormValue("grant_type")
	redirectUri := r.FormValue("redirect_uri")

	log.Printf("Redirect URI is %s\n", redirectUri)

	switch grantType {
	case "authorization_code":
		code := r.FormValue("code")

		fmt.Printf("Code received: %s\n", code)

		// TODO: Verify code from another db table
		nonce := strings.Split(code, ",")[1]

		userId := strings.Split(code, ",")[0]

		user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(userId))
		if err != nil {
			web.Error(w, "requested user not found", http.StatusNotFound)
			return
		}

		var roles = []string{"user"}

		if user.IsAdmin {
			roles = append(roles, "admin")
		}

		claims := types.ApiClaims{
			Email: user.Email,
			// TODO:
			EmailVerified: true,
			Name:          user.FullName,
			Picture:       user.ProfilePicture,
			Nonce:         nonce,
			Roles:         roles,
			RegisteredClaims: jwt.RegisteredClaims{
				Issuer: h.cfg.Uri,
				// TODO: use dedicated API id that is in local DB and bind to user there
				Subject:   user.ID.String(),
				Audience:  jwt.ClaimStrings{clientId},
				IssuedAt:  jwt.NewNumericDate(time.Now()),
				ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
			},
		}

		idToken, err := util.SignJwtToken(claims, h.cfg.Jwt.PrivateKey)
		if err != nil {
			web.Error(w, "failed to sign id token", http.StatusInternalServerError)
			return
		}

		type Response struct {
			IdToken     string `json:"id_token"`
			TokenType   string `json:"token_type"`
			AccessToken string `json:"access_token"`
			Email       string `json:"email"`
			// TODO: add expires_in, refresh_token, scope (RFC 8693 $2)
		}

		response := Response{
			IdToken:   idToken,
			TokenType: "Bearer",
			// FIXME:
			AccessToken: idToken,
			Email:       user.Email,
		}

		log.Printf("sending following response: %#v\n", response)

		w.Header().Set("Content-Type", "application/json")
		encoder := json.NewEncoder(w)
		if err := encoder.Encode(response); err != nil {
			web.Error(w, "failed to encode response", http.StatusInternalServerError)
		}
	default:
		web.Error(w, "unsupported grant type", http.StatusBadRequest)
	}
}
