// Package providertokens exposes user-owned external access token APIs.
package providertokens

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api/meshxapi"
	"github.com/tidbcloud/AgentTeam/internal/service/providertokens"
)

type Handler struct{ service *providertokens.Service }
type RouteMiddleware func(http.Handler) http.Handler
type routeRegistrar interface {
	Handle(pattern string, handler http.Handler)
}

type CreateRequest struct {
	DisplayName     string     `json:"displayName"`
	ProviderType    string     `json:"providerType"`
	ProviderBaseURL string     `json:"providerBaseUrl"`
	Username        string     `json:"username"`
	AllowedUsages   []string   `json:"allowedUsages"`
	AccessToken     string     `json:"accessToken"`
	ExpiresAt       *time.Time `json:"expiresAt"`
}

type UpdateRequest struct {
	DisplayName     *string    `json:"displayName"`
	ProviderType    *string    `json:"providerType"`
	ProviderBaseURL *string    `json:"providerBaseUrl"`
	Username        *string    `json:"username"`
	AllowedUsages   []string   `json:"allowedUsages"`
	AccessToken     *string    `json:"accessToken"`
	ExpiresAt       *time.Time `json:"expiresAt"`
	ClearExpiresAt  bool       `json:"clearExpiresAt"`
}

func NewHandler(svc *providertokens.Service) *Handler { return &Handler{service: svc} }

func RegisterRoutes(router routeRegistrar, handler *Handler, middleware RouteMiddleware) {
	if router == nil {
		return
	}
	if handler == nil {
		handler = NewHandler(nil)
	}
	register(router, "GET /api/v1/personal-access-tokens", meshxapi.HandleAuth(handler.List, errorHandling()), middleware)
	register(router, "POST /api/v1/personal-access-tokens", meshxapi.HandleAuthJSON[CreateRequest](handler.Create, errorHandling()), middleware)
	register(router, "GET /api/v1/personal-access-tokens/{token_id}", meshxapi.HandleAuth(handler.Get, errorHandling()), middleware)
	register(router, "PATCH /api/v1/personal-access-tokens/{token_id}", meshxapi.HandleAuthJSON[UpdateRequest](handler.Update, errorHandling()), middleware)
	register(router, "POST /api/v1/personal-access-tokens/{token_id}/revoke", meshxapi.HandleAuth(handler.Revoke, errorHandling()), middleware)
	register(router, "DELETE /api/v1/personal-access-tokens/{token_id}", meshxapi.HandleAuth(handler.Delete, errorHandling()), middleware)
}

func register(router routeRegistrar, pattern string, handler http.Handler, middleware RouteMiddleware) {
	if middleware != nil {
		handler = middleware(handler)
	}
	router.Handle(pattern, handler)
}

func (h *Handler) svc() *providertokens.Service {
	if h == nil || h.service == nil {
		return providertokens.New(nil, providertokens.Options{})
	}
	return h.service
}

func (h *Handler) List(r meshxapi.AuthenticatedRequest) (meshxapi.Response, error) {
	limit, _ := strconv.Atoi(strings.TrimSpace(r.Request.URL.Query().Get("limit")))
	result, err := h.svc().List(r.Context(), providertokens.ListRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		Usage:            strings.TrimSpace(r.Request.URL.Query().Get("usage")),
		ProviderBaseURL:  strings.TrimSpace(r.Request.URL.Query().Get("providerBaseUrl")),
		Status:           strings.TrimSpace(r.Request.URL.Query().Get("status")),
		Limit:            limit,
	})
	if err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.OK(result), nil
}

func (h *Handler) Create(r meshxapi.AuthenticatedJSON[CreateRequest]) (meshxapi.Response, error) {
	result, err := h.svc().Create(r.Context(), providertokens.CreateRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		DisplayName:      r.Body.DisplayName,
		ProviderType:     r.Body.ProviderType,
		ProviderBaseURL:  r.Body.ProviderBaseURL,
		Username:         r.Body.Username,
		AllowedUsages:    r.Body.AllowedUsages,
		AccessToken:      r.Body.AccessToken,
		ExpiresAt:        r.Body.ExpiresAt,
	})
	if err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.Created(map[string]providertokens.TokenRef{"token": result}), nil
}

func (h *Handler) Get(r meshxapi.AuthenticatedRequest) (meshxapi.Response, error) {
	result, err := h.svc().Get(r.Context(), providertokens.GetRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		ID:               r.Request.PathValue("token_id"),
	})
	if err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.OK(result), nil
}

func (h *Handler) Update(r meshxapi.AuthenticatedJSON[UpdateRequest]) (meshxapi.Response, error) {
	result, err := h.svc().Update(r.Context(), providertokens.UpdateRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		ID:               r.Request.PathValue("token_id"),
		DisplayName:      r.Body.DisplayName,
		ProviderType:     r.Body.ProviderType,
		ProviderBaseURL:  r.Body.ProviderBaseURL,
		Username:         r.Body.Username,
		AllowedUsages:    r.Body.AllowedUsages,
		AccessToken:      r.Body.AccessToken,
		ExpiresAt:        r.Body.ExpiresAt,
		ClearExpiresAt:   r.Body.ClearExpiresAt,
	})
	if err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.OK(map[string]providertokens.TokenRef{"token": result}), nil
}

func (h *Handler) Revoke(r meshxapi.AuthenticatedRequest) (meshxapi.Response, error) {
	result, err := h.svc().Revoke(r.Context(), providertokens.RevokeRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		ID:               r.Request.PathValue("token_id"),
	})
	if err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.OK(map[string]providertokens.TokenRef{"token": result}), nil
}

func (h *Handler) Delete(r meshxapi.AuthenticatedRequest) (meshxapi.Response, error) {
	if err := h.svc().Delete(r.Context(), providertokens.DeleteRequest{
		WorkspaceID:      r.WorkspaceID(),
		ActorPrincipalID: r.Subject(),
		ID:               r.Request.PathValue("token_id"),
	}); err != nil {
		return meshxapi.Response{}, err
	}
	return meshxapi.NoContent(), nil
}

func errorHandling() meshxapi.HandlerOption {
	return meshxapi.WithErrorClassifier(func(err error) (meshxapi.ErrorResponse, bool) {
		status := providertokens.HTTPStatus(err)
		if status == http.StatusInternalServerError {
			return meshxapi.ErrorResponse{}, false
		}
		return meshxapi.ErrorResponse{Status: status, Message: err.Error(), Code: providertokens.Code(err)}, true
	})
}
