package handlers

import (
	"net/http"
	"strconv"

	"xorm.io/xorm"

	"github.com/forgeo/forgebucket/internal/models"
)

type ExploreHandler struct{ db *xorm.Engine }

func NewExploreHandler(db *xorm.Engine) *ExploreHandler { return &ExploreHandler{db: db} }

type exploreRepo struct {
	ID             int64  `json:"id"`
	Name           string `json:"name"`
	Description    string `json:"description"`
	IsPrivate      bool   `json:"isPrivate"`
	DefaultBranch  string `json:"defaultBranch"`
	CreatedAt      string `json:"createdAt"`
	UpdatedAt      string `json:"updatedAt"`
	OwnerID        int64  `json:"ownerId"`
	OwnerUsername  string `json:"ownerUsername"`
	OwnerAvatarURL string `json:"ownerAvatarUrl"`
}

type exploreUser struct {
	ID        int64  `json:"id"`
	Username  string `json:"username"`
	AvatarURL string `json:"avatarUrl"`
	CreatedAt string `json:"createdAt"`
}

func (h *ExploreHandler) Repos(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query().Get("q")
	sort := r.URL.Query().Get("sort")
	limit := clampInt(r.URL.Query().Get("limit"), 20, 1, 50)
	offset := clampInt(r.URL.Query().Get("offset"), 0, 0, 1<<30)

	sess := h.db.Where("is_private = false")
	if q != "" {
		sess = sess.And("(name ILIKE ? OR description ILIKE ?)", "%"+q+"%", "%"+q+"%")
	}
	switch sort {
	case "name":
		sess = sess.Asc("name")
	case "created":
		sess = sess.Desc("created_at")
	default:
		sess = sess.Desc("updated_at")
	}

	var repos []models.Repository
	if err := sess.Limit(limit, offset).Find(&repos); err != nil {
		jsonError(w, "could not search repositories", http.StatusInternalServerError)
		return
	}

	results := make([]exploreRepo, 0, len(repos))
	for _, repo := range repos {
		var owner models.User
		found, _ := h.db.ID(repo.OwnerID).Cols("id", "username", "avatar_url").Get(&owner)
		if !found {
			continue
		}
		results = append(results, exploreRepo{
			ID:             repo.ID,
			Name:           repo.Name,
			Description:    repo.Description,
			IsPrivate:      repo.IsPrivate,
			DefaultBranch:  repo.DefaultBranch,
			CreatedAt:      repo.CreatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedAt:      repo.UpdatedAt.Format("2006-01-02T15:04:05Z"),
			OwnerID:        owner.ID,
			OwnerUsername:  owner.Username,
			OwnerAvatarURL: owner.AvatarURL,
		})
	}
	jsonOK(w, results)
}

func (h *ExploreHandler) Users(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query().Get("q")
	limit := clampInt(r.URL.Query().Get("limit"), 20, 1, 50)
	offset := clampInt(r.URL.Query().Get("offset"), 0, 0, 1<<30)

	sess := h.db.Cols("id", "username", "avatar_url", "created_at")
	if q != "" {
		sess = sess.Where("username ILIKE ?", "%"+q+"%")
	} else {
		sess = sess.Where("1 = 1")
	}
	sess = sess.Asc("username")

	var users []models.User
	if err := sess.Limit(limit, offset).Find(&users); err != nil {
		jsonError(w, "could not search users", http.StatusInternalServerError)
		return
	}

	results := make([]exploreUser, 0, len(users))
	for _, u := range users {
		results = append(results, exploreUser{
			ID:        u.ID,
			Username:  u.Username,
			AvatarURL: u.AvatarURL,
			CreatedAt: u.CreatedAt.Format("2006-01-02T15:04:05Z"),
		})
	}
	jsonOK(w, results)
}

func clampInt(s string, def, min, max int) int {
	v, err := strconv.Atoi(s)
	if err != nil {
		return def
	}
	if v < min {
		return min
	}
	if v > max {
		return max
	}
	return v
}
