package auth

import (
	"context"
	"net/http"
	"strings"

	"leadb2b.org/internal/api"
)

const CookieName = "leadb2b_token"

type Session struct {
	Token string
	Me    *api.MeResponse
}

type Service struct {
	authAPI *api.AuthService
}

func NewService(authAPI *api.AuthService) *Service {
	return &Service{authAPI: authAPI}
}

func (s *Session) Limits() *api.LimitsInfo {
	if s.Me != nil && s.Me.Product != nil {
		return &s.Me.Product.Limits
	}
	return nil
}

func (s *Session) IsAdmin() bool {
	return s.Me != nil && s.Me.Product != nil && s.Me.Product.Role == "admin"
}

func (s *Session) IsTrialPlan() bool {
	if s.Me == nil || s.Me.Product == nil {
		return false
	}
	plan := strings.ToLower(strings.TrimSpace(s.Me.Product.Subscription.Plan))
	status := strings.ToLower(strings.TrimSpace(s.Me.Product.Subscription.Status))
	return plan == "trial" || plan == "free" || status == "trial"
}

func (s *Session) DisplayName() string {
	if s.Me != nil && s.Me.Product != nil && s.Me.Product.Name != nil {
		return strings.TrimSpace(*s.Me.Product.Name)
	}
	return ""
}

func (s *Service) SetTokenCookie(w http.ResponseWriter, token string) {
	http.SetCookie(w, &http.Cookie{
		Name:     CookieName,
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		SameSite: http.SameSiteLaxMode,
		MaxAge:   7 * 24 * 3600,
	})
}

func (s *Service) ClearTokenCookie(w http.ResponseWriter) {
	http.SetCookie(w, &http.Cookie{
		Name:     CookieName,
		Value:    "",
		Path:     "/",
		HttpOnly: true,
		MaxAge:   -1,
	})
}

func (s *Service) TokenFromRequest(r *http.Request) string {
	if c, err := r.Cookie(CookieName); err == nil && c.Value != "" {
		return c.Value
	}
	h := r.Header.Get("Authorization")
	if strings.HasPrefix(strings.ToLower(h), "bearer ") {
		return strings.TrimSpace(h[7:])
	}
	return ""
}

func (s *Service) Register(ctx context.Context, email, password, name string) (*Session, error) {
	tok, err := s.authAPI.Register(ctx, api.RegisterRequest{Email: email, Password: password, Name: name})
	if err != nil {
		return nil, err
	}
	return s.ResolveSession(ctx, tok.AccessToken)
}

func (s *Service) Login(ctx context.Context, email, password string) (*Session, error) {
	tok, err := s.authAPI.Login(ctx, api.LoginRequest{Email: email, Password: password})
	if err != nil {
		return nil, err
	}
	return s.ResolveSession(ctx, tok.AccessToken)
}

func (s *Service) Logout(ctx context.Context, token string) {
	_ = s.authAPI.Logout(ctx, token)
}

func (s *Service) ResolveSession(ctx context.Context, token string) (*Session, error) {
	if token == "" {
		return nil, ErrUnauthorized
	}
	me, err := s.authAPI.Me(ctx, token)
	if err != nil {
		return nil, err
	}
	if me.Product != nil && me.Product.Status == "suspended" {
		return nil, ErrSuspended
	}
	return &Session{Token: token, Me: me}, nil
}

func (s *Service) ForgotPassword(ctx context.Context, email string) error {
	return s.authAPI.ForgotPassword(ctx, email)
}

func (s *Service) ResetPassword(ctx context.Context, token, newPassword string) error {
	return s.authAPI.ResetPassword(ctx, token, newPassword)
}

func (s *Service) UpdateProfile(ctx context.Context, token, name string) (*Session, error) {
	n := strings.TrimSpace(name)
	me, err := s.authAPI.UpdateMe(ctx, token, api.UpdateMeRequest{Name: &n})
	if err != nil {
		return nil, err
	}
	return &Session{Token: token, Me: me}, nil
}

type contextKey string

const sessionKey contextKey = "leadb2b_session"

func WithSession(ctx context.Context, sess *Session) context.Context {
	return context.WithValue(ctx, sessionKey, sess)
}

func SessionFromContext(ctx context.Context) *Session {
	s, _ := ctx.Value(sessionKey).(*Session)
	return s
}
