package oauth

import (
	gocontext "context"
	gojson "encoding/json"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/gitea"
	"github.com/rs/zerolog/log"
	"github.com/thomiceli/opengist/internal/config"
	"github.com/thomiceli/opengist/internal/db"
	"github.com/thomiceli/opengist/internal/web/context"
	"io"
	"net/http"
)

type GiteaProvider struct {
	Provider
	URL string
}

func (p *GiteaProvider) RegisterProvider() error {
	goth.UseProviders(
		gitea.NewCustomisedURL(
			config.C.GiteaClientKey,
			config.C.GiteaSecret,
			urlJoin(p.URL, "/oauth/gitea/callback"),
			urlJoin(config.C.GiteaUrl, "/login/oauth/authorize"),
			urlJoin(config.C.GiteaUrl, "/login/oauth/access_token"),
			urlJoin(config.C.GiteaUrl, "/api/v1/user"),
		),
	)

	return nil
}

func (p *GiteaProvider) BeginAuthHandler(ctx *context.Context) {
	ctxValue := gocontext.WithValue(ctx.Request().Context(), gothic.ProviderParamKey, GiteaProviderString)
	ctx.SetRequest(ctx.Request().WithContext(ctxValue))

	gothic.BeginAuthHandler(ctx.Response(), ctx.Request())
}

func (p *GiteaProvider) UserHasProvider(user *db.User) bool {
	return user.GiteaID != ""
}

func NewGiteaProvider(url string) *GiteaProvider {
	return &GiteaProvider{
		URL: url,
	}
}

type GiteaCallbackProvider struct {
	CallbackProvider
	User *goth.User
}

func (p *GiteaCallbackProvider) GetProvider() string {
	return GiteaProviderString
}

func (p *GiteaCallbackProvider) GetProviderUser() *goth.User {
	return p.User
}

func (p *GiteaCallbackProvider) GetProviderUserID(user *db.User) bool {
	return user.GiteaID != ""
}

func (p *GiteaCallbackProvider) GetProviderUserSSHKeys() ([]string, error) {
	resp, err := http.Get(urlJoin(config.C.GiteaUrl, p.User.NickName+".keys"))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return readKeys(resp)
}

func (p *GiteaCallbackProvider) UpdateUserDB(user *db.User) {
	user.GiteaID = p.User.UserID

	resp, err := http.Get(urlJoin(config.C.GiteaUrl, "/api/v1/users/", p.User.UserID))
	if err != nil {
		log.Error().Err(err).Msg("Cannot get user from Gitea")
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error().Err(err).Msg("Cannot read Gitea response body")
		return
	}

	var result map[string]interface{}
	err = gojson.Unmarshal(body, &result)
	if err != nil {
		log.Error().Err(err).Msg("Cannot unmarshal Gitea response body")
		return
	}

	field, ok := result["avatar_url"]
	if !ok {
		log.Error().Msg("Field 'avatar_url' not found in Gitea JSON response")
		return
	}

	user.AvatarURL = field.(string)
}

func (p *GiteaCallbackProvider) IsAdmin() bool {
	return false
}

func NewGiteaCallbackProvider(user *goth.User) CallbackProvider {
	return &GiteaCallbackProvider{
		User: user,
	}
}
