package auth

import (
	"errors"
	"time"

	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/repository"
	authcore "hyapp-admin-server/internal/service"

	"gorm.io/gorm"
)

type AuthFlowService struct {
	store authStore
	auth  *authcore.AuthService
	cfg   config.Config
}

type authStore interface {
	FindUserByUsername(username string) (*model.User, error)
	FindUserByID(id uint) (*model.User, error)
	CreateRefreshToken(token model.RefreshToken) error
	RotateRefreshToken(oldTokenID uint, token model.RefreshToken) error
	FindRefreshToken(hash string) (*model.RefreshToken, error)
	RevokeRefreshToken(hash string) error
	TouchUserLogin(id uint) error
	CreateLoginLog(log model.LoginLog) error
	ResetUserPassword(id uint, password string) error
}

type LoginInput struct {
	Username  string
	Password  string
	IP        string
	UserAgent string
}

type LoginResult struct {
	AccessToken  string
	RefreshToken string
	ExpiresAtMS  int64
	User         model.User
	Permissions  []string
}

func NewService(store authStore, auth *authcore.AuthService, cfg config.Config) *AuthFlowService {
	return &AuthFlowService{store: store, auth: auth, cfg: cfg}
}

func (s *AuthFlowService) Login(input LoginInput) (LoginResult, error) {
	user, err := s.store.FindUserByUsername(input.Username)
	if err != nil || !authcore.CheckPassword(user.PasswordHash, input.Password) {
		s.loginLog(input, nil, "failed", "用户名或密码错误")
		return LoginResult{}, errors.New("用户名或密码错误")
	}
	if user.Status != model.UserStatusActive {
		s.loginLog(input, &user.ID, "failed", "账号不可登录")
		return LoginResult{}, errors.New("账号不可登录")
	}

	permissions := repository.PermissionsForUser(*user)
	accessToken, expiresAt, err := s.auth.GenerateAccessToken(user.ID, user.Username, permissions)
	if err != nil {
		return LoginResult{}, err
	}
	refreshToken, refreshRecord, err := s.newRefreshToken(user.ID, input.IP, input.UserAgent)
	if err != nil {
		return LoginResult{}, err
	}
	if err := s.store.CreateRefreshToken(refreshRecord); err != nil {
		return LoginResult{}, err
	}
	_ = s.store.TouchUserLogin(user.ID)
	s.loginLog(input, &user.ID, "success", "登录成功")
	return LoginResult{AccessToken: accessToken, RefreshToken: refreshToken, ExpiresAtMS: expiresAt.UnixMilli(), User: *user, Permissions: permissions}, nil
}

func (s *AuthFlowService) Logout(refreshToken string) error {
	if refreshToken == "" {
		return nil
	}
	return s.store.RevokeRefreshToken(authcore.HashToken(refreshToken))
}

func (s *AuthFlowService) Refresh(refreshToken string, input LoginInput) (LoginResult, error) {
	if refreshToken == "" {
		return LoginResult{}, errors.New("刷新会话不存在")
	}
	stored, err := s.store.FindRefreshToken(authcore.HashToken(refreshToken))
	if err != nil {
		return LoginResult{}, errors.New("刷新会话已失效")
	}
	user, err := s.store.FindUserByID(stored.UserID)
	if err != nil || user.Status != model.UserStatusActive {
		return LoginResult{}, errors.New("账号不可登录")
	}
	permissions := repository.PermissionsForUser(*user)
	accessToken, expiresAt, err := s.auth.GenerateAccessToken(user.ID, user.Username, permissions)
	if err != nil {
		return LoginResult{}, err
	}
	newRefreshToken, refreshRecord, err := s.newRefreshToken(user.ID, input.IP, input.UserAgent)
	if err != nil {
		return LoginResult{}, err
	}
	if err := s.store.RotateRefreshToken(stored.ID, refreshRecord); err != nil {
		return LoginResult{}, err
	}
	return LoginResult{AccessToken: accessToken, RefreshToken: newRefreshToken, ExpiresAtMS: expiresAt.UnixMilli(), User: *user, Permissions: permissions}, nil
}

func (s *AuthFlowService) newRefreshToken(userID uint, ip string, userAgent string) (string, model.RefreshToken, error) {
	refreshToken, refreshHash, err := authcore.NewRefreshToken()
	if err != nil {
		return "", model.RefreshToken{}, err
	}
	return refreshToken, model.RefreshToken{
		UserID:      userID,
		TokenHash:   refreshHash,
		UserAgent:   userAgent,
		IP:          ip,
		ExpiresAtMS: time.Now().UTC().Add(s.cfg.RefreshTokenTTL).UnixMilli(),
	}, nil
}

func (s *AuthFlowService) Me(actor shared.Actor) (*model.User, []string, error) {
	user, err := s.store.FindUserByID(actor.UserID)
	if err != nil {
		return nil, nil, err
	}
	return user, repository.PermissionsForUser(*user), nil
}

func (s *AuthFlowService) ChangePassword(actor shared.Actor, oldPassword string, newPassword string) error {
	user, err := s.store.FindUserByID(actor.UserID)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.New("用户不存在")
	}
	if err != nil {
		return err
	}
	if !authcore.CheckPassword(user.PasswordHash, oldPassword) {
		return errors.New("旧密码不正确")
	}
	return s.store.ResetUserPassword(user.ID, newPassword)
}

func (s *AuthFlowService) loginLog(input LoginInput, userID *uint, status string, message string) {
	_ = s.store.CreateLoginLog(model.LoginLog{
		Username:  input.Username,
		UserID:    userID,
		IP:        input.IP,
		UserAgent: input.UserAgent,
		Status:    status,
		Message:   message,
	})
}
