/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 */
package handler

import (
	"context"
	"fmt"
	"regexp"

	"mstore/app/router"
	"mstore/pkg/auxhttp"
	"mstore/pkg/auxpwd"
	"mstore/pkg/terms"
)

const (
	authTag = "authpass"
	userTag = "accountID"
)

func (hand *Handler) AuthMiddleware(next router.Handler) router.Handler {
	var handlerFunc router.HandlerFunc

	handlerFunc = func(rctx *router.Context) {
		success, accountID, err := hand.CheckAccess(rctx)
		if success {
			rctx.SetBool(authTag, true)
			rctx.SetString(userTag, string(accountID))
		}
		if err != nil {
			hand.logg.Errorf("Authorization middleware error: %v", err)
		}
		next.ServeHTTP(rctx)
	}
	return handlerFunc
}

// Authentification
func (hand *Handler) CheckAccess(rctx *router.Context) (bool, string, error) {
	var err error
	var success bool
	var username string
	var password string
	var accountID string

	accountID = terms.AnonymousID

	//hand.logg.Debugf("URL: %s", rctx.URL().String())
	authHeader := rctx.GetHeader("Authorization")
	hand.logg.Debugf("Authorization: [%s]", authHeader)
	if authHeader != "" {
		username, password, err = auxhttp.ParseBasicAuth(authHeader)
		if err != nil {
			return success, accountID, err
		}
		if username == "" || password == "" {
			goto anonymous
		}

		success, id, err := hand.ValidatePassword(rctx.Ctx, username, password)
		if err != nil {
			return false, accountID, err
		}
		if !success {
			err = fmt.Errorf("Incorrect username or password")
			return false, accountID, err
		}
		accountID = id
		return success, accountID, err
	}
anonymous:
	success = true
	accountID = terms.AnonymousID
	return success, accountID, err
}

func (hand *Handler) ValidatePassword(ctx context.Context, username, password string) (bool, string, error) {
	var err error
	var accountID string
	valid := false

	accountExists, accountDescr, err := hand.mdb.GetAccountByUsername(ctx, username)
	if !accountExists {
		err := fmt.Errorf("Account not exists")
		return valid, accountID, err
	}
	if !auxpwd.PasswordMatch([]byte(password), accountDescr.Passhash) {
		err := fmt.Errorf("Login data mismatch")
		return valid, accountID, err
	}
	valid = true
	accountID = accountDescr.ID

	return valid, accountID, err
}

// Authorization

func (hand *Handler) CheckRight(ctx context.Context, accountID, reqRight, subject string) (bool, error) {
	var err error
	var res bool
	hand.logg.Debugf("Check right %s: right=[%s] subj=[%s]", accountID, reqRight, subject)
	exists, grants, err := hand.mdb.ListGrantsByAccoundIDRight(ctx, accountID, reqRight)
	if err != nil {
		return res, err
	}
	if !exists {
		return res, err
	}
	switch reqRight {
	case terms.RightReadFiles, terms.RightWriteFiles, terms.RightReadImages, terms.RightWriteImages:
		for _, grant := range grants {
			//hand.logg.Debugf("Grant accountID=[%s] pattern=[%s], subject=[%s]", accountID, grant.Pattern, subject)

			re, err := regexp.Compile(grant.Pattern)
			if err != nil {
				hand.logg.Warningf("Wrong regexp %s for grant %s: %v", grant.Pattern, grant.ID, err)
				err = nil
				continue
			}
			if re.MatchString(subject) {
				res = true
				break
			}
		}
	default:
		res = true
	}
	return res, err
}
