package webdebug

import (
	"crypto/rand"
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	webutils "git.mstar.dev/mstar/goutils/http"
	"git.mstar.dev/mstar/goutils/other"
	"git.mstar.dev/mstar/goutils/sliceutils"
	"github.com/rs/zerolog/hlog"

	"git.mstar.dev/mstar/linstrom/activitypub"
	"git.mstar.dev/mstar/linstrom/shared"
	"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
	"git.mstar.dev/mstar/linstrom/storage-new/models"
	webshared "git.mstar.dev/mstar/linstrom/web/shared"
)

func getNonDeletedUsers(w http.ResponseWriter, r *http.Request) {
	log := hlog.FromRequest(r)
	pageStr := r.FormValue("page")
	page := 0
	if pageStr != "" {
		var err error
		page, err = strconv.Atoi(pageStr)
		if err != nil {
			webutils.ProblemDetails(
				w,
				http.StatusBadRequest,
				"/errors/bad-page",
				"bad page number",
				other.IntoPointer("page number must be an uint"),
				nil,
			)
			return
		}
		if page < 0 {
			webutils.ProblemDetails(
				w,
				http.StatusBadRequest,
				"/errors/bad-page",
				"bad page number",
				other.IntoPointer("page number must be >= 0"),
				nil,
			)
			return
		}
	}
	users, err := dbgen.User.GetPagedAllNonDeleted(uint(page))
	if err != nil {
		log.Error().Err(err).Int("page", page).Msg("Failed to get non-deleted users")
		webutils.ProblemDetails(
			w,
			http.StatusInternalServerError,
			"/errors/db-failure",
			"database failure",
			nil,
			nil,
		)
		return
	}
	webutils.SendJson(w, sliceutils.Map(users, func(t models.User) webshared.User {
		u := webshared.User{}
		u.FromModel(&t)
		return u
	}))
}

func createLocalUser(w http.ResponseWriter, r *http.Request) {
	type Inbound struct {
		Username    string     `json:"username"`
		Displayname string     `json:"displayname"`
		Description string     `json:"description"`
		Birthday    *time.Time `json:"birthday"`
		Location    *string    `json:"location"`
		IsBot       bool       `json:"is_bot"`
	}
	log := hlog.FromRequest(r)
	jsonDecoder := json.NewDecoder(r.Body)
	data := Inbound{}
	err := jsonDecoder.Decode(&data)
	if err != nil {
		webutils.ProblemDetails(
			w,
			http.StatusBadRequest,
			"/errors/bad-request-data",
			"bad request data",
			nil,
			map[string]any{
				"sample": Inbound{
					Username:    "bob",
					Displayname: "Bob Bobbington",
					Description: "Bobbing Bobs bop to Bobs bobbing beats",
					Birthday:    other.IntoPointer(time.Now()),
					Location:    nil,
					IsBot:       false,
				},
			},
		)
		return
	}

	publicKeyEdBytes, privateKeyEdBytes, err := shared.GenerateKeypair(true)
	if err != nil {
		log.Error().Err(err).Msg("Failed to generate and marshal public key")
		webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
		return
	}
	publicKeyRsaBytes, privateKeyRsaBytes, err := shared.GenerateKeypair(false)
	if err != nil {
		log.Error().Err(err).Msg("Failed to generate and marshal public key")
		webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
		return
	}
	pkeyId := make([]byte, 64)
	_, err = rand.Read(pkeyId)
	if err != nil {
		log.Error().Err(err).Msg("Failed to generate passkey id")
		webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
		return
	}

	u := dbgen.User
	query := u.Select(
		u.Username,
		u.DisplayName,
		u.Description,
		u.IsBot,
		u.ServerId,
		u.PrivateKeyEd,
		u.PublicKeyEd,
		u.PrivateKeyRsa,
		u.PublicKeyRsa,
		u.PasskeyId,
	)
	if data.Birthday != nil {
		query = query.Select(u.Birthday)
	}
	if data.Location != nil {
		query = query.Select(u.Location)
	}
	user := models.User{
		Username:      data.Username,
		DisplayName:   data.Displayname,
		Description:   data.Description,
		IsBot:         data.IsBot,
		ServerId:      1, // Hardcoded, Self is always first ID
		PublicKeyRsa:  publicKeyRsaBytes,
		PublicKeyEd:   publicKeyEdBytes,
		PrivateKeyRsa: privateKeyRsaBytes,
		PrivateKeyEd:  privateKeyEdBytes,
		PasskeyId:     pkeyId,
	}
	if data.Birthday != nil {
		user.Birthday = sql.NullTime{Valid: true, Time: *data.Birthday}
	}
	if data.Location != nil {
		user.Location = sql.NullString{Valid: true, String: *data.Location}
	}
	if err = u.Create(&user); err != nil {
		log.Error().Err(err).Msg("failed to create new local user")
		webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
	}
}

func deleteUser(w http.ResponseWriter, r *http.Request) {
	id := r.FormValue("id")
	dbgen.User.Where(dbgen.User.ID.Eq(id)).Delete()
	w.WriteHeader(http.StatusOK)
}

func issueUserImport(w http.ResponseWriter, r *http.Request) {
	target := r.FormValue("target")
	_, err := activitypub.ImportRemoteAccount(target)
	hlog.FromRequest(r).Info().Err(err).Msg("Err from import request")
}

func kickoffFollow(w http.ResponseWriter, r *http.Request) {
	type Inbound struct {
		Id     string
		Target string
	}
	var data Inbound
	dec := json.NewDecoder(r.Body)
	dec.Decode(&data)
}
