package adminauth

import (
	"context"
	"errors"

	"hfb_sys/backend/internal/modules/auth"
)

var (
	ErrDependencyUnavailable = errors.New("dependency unavailable")
	ErrInvalidCredential     = errors.New("invalid credential")
	ErrCaptchaInvalid        = errors.New("captcha invalid")
	ErrAdminDisabled         = errors.New("admin disabled")
	ErrInvalidRefreshToken   = errors.New("invalid refresh token")
	ErrLoginLocked           = errors.New("login locked")
)

type Service struct {
	repo *Repository
	jwt  *auth.JWTManager
}

func NewService(repo *Repository, jwt *auth.JWTManager) *Service {
	return &Service{repo: repo, jwt: jwt}
}

func (s *Service) Refresh(ctx context.Context, refreshToken string) (*auth.TokenPair, error) {
	if s.jwt == nil {
		return nil, ErrDependencyUnavailable
	}
	claims, err := s.jwt.ParseSubject(refreshToken, "refresh", "admin")
	if err != nil {
		return nil, ErrInvalidRefreshToken
	}
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	admin, err := s.repo.FindActiveForToken(ctx, claims.UserID, claims.TokenVersion)
	if err != nil {
		if errors.Is(err, ErrAdminDisabled) {
			return nil, ErrAdminDisabled
		}
		return nil, ErrInvalidRefreshToken
	}
	pair, err := s.jwt.GenerateSubjectPairWithVersion(admin.ID, admin.Username, "admin", admin.TokenVersion)
	if err != nil {
		return nil, err
	}
	return &pair, nil
}

func (s *Service) Captcha(ctx context.Context) (*CaptchaDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.Captcha(ctx)
}

func (s *Service) Login(ctx context.Context, username string, password string, captchaID string, captchaCode string, clientIP string) (LoginResult, error) {
	if s.repo == nil {
		return LoginResult{}, ErrDependencyUnavailable
	}
	if username == "" || password == "" || captchaID == "" || captchaCode == "" {
		return LoginResult{}, ErrInvalidCredential
	}
	return s.repo.Login(ctx, username, password, captchaID, captchaCode, clientIP)
}

func (s *Service) Me(ctx context.Context, adminID uint64) (*AdminDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.FindByID(ctx, adminID)
}

func (s *Service) UpdateSupportStatus(ctx context.Context, adminID uint64, status string) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.UpdateSupportStatus(ctx, adminID, status)
}

func (s *Service) Logout(ctx context.Context, adminID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.RevokeTokens(ctx, adminID)
}
