package mail

import (
	"encoding/json"
	"net/http"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
	"github.com/ultisuite/ulti-backend/internal/api/middleware"
	"github.com/ultisuite/ulti-backend/internal/apitokens"
)

type createApiTokenRequest struct {
	Name        string                     `json:"name"`
	Permissions []apitokens.PermissionGrant `json:"permissions"`
	MailScope   apitokens.MailScope        `json:"mail_scope"`
	DriveScope  apitokens.DriveScope       `json:"drive_scope"`
	ExpiresAt   *time.Time                 `json:"expires_at,omitempty"`
}

func (h *Handler) ListApiTokens(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	db := h.db()
	if db == nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
		return
	}
	tokens, err := apitokens.List(r.Context(), db, claims.Sub)
	if err != nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to list api tokens", nil)
		return
	}
	apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"tokens": tokens})
}

func (h *Handler) CreateApiToken(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())

	var req createApiTokenRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid json", nil)
		return
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(apivalidate.FieldDetail{
			Field: "name", Message: "name is required",
		}))
		return
	}
	if !hasAnyPermission(req.Permissions) {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(apivalidate.FieldDetail{
			Field: "permissions", Message: "at least one permission is required",
		}))
		return
	}

	db := h.db()
	if db == nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
		return
	}

	created, err := apitokens.Create(
		r.Context(),
		db,
		claims.Sub,
		name,
		req.Permissions,
		normalizeMailScope(req.MailScope),
		normalizeDriveScope(req.DriveScope),
		req.ExpiresAt,
	)
	if err != nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to create api token", nil)
		return
	}

	apiresponse.WriteJSON(w, http.StatusCreated, created)
}

func (h *Handler) RevokeApiToken(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	tokenID := chi.URLParam(r, "tokenID")

	db := h.db()
	if db == nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
		return
	}
	if err := apitokens.Revoke(r.Context(), db, claims.Sub, tokenID); err != nil {
		if err == apitokens.ErrNotFound {
			apiresponse.WriteError(w, r, http.StatusNotFound, apiresponse.CodeNotFound, "api token not found", nil)
			return
		}
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to revoke api token", nil)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func hasAnyPermission(grants []apitokens.PermissionGrant) bool {
	for _, g := range grants {
		if g.Read || g.Write {
			return true
		}
	}
	return false
}

func normalizeMailScope(scope apitokens.MailScope) apitokens.MailScope {
	if scope.AllAccounts || len(scope.AccountIDs) == 0 {
		return apitokens.MailScope{AllAccounts: true, AccountIDs: nil}
	}
	return scope
}

func normalizeDriveScope(scope apitokens.DriveScope) apitokens.DriveScope {
	if scope.AllFolders || len(scope.FolderPaths) == 0 {
		return apitokens.DriveScope{AllFolders: true, FolderPaths: nil}
	}
	return scope
}

func (h *Handler) db() *pgxpool.Pool {
	if s, ok := h.svc.(*Service); ok {
		return s.DB()
	}
	return nil
}
