/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 *
 * This work is published and licensed under a Creative Commons
 * Attribution-NonCommercial-NoDerivatives 4.0 International License.
 *
 * Distribution of this work is permitted, but commercial use and
 * modifications are strictly prohibited.
 */
package handler

import (
	"context"
	"fmt"
	"regexp"

	"mstore/app/router"
	"mstore/pkg/auxhttp"
	"mstore/pkg/auxpwd"
	"mstore/pkg/term"
	"mstore/pkg/uuid"
)

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, uuid.UUID, error) {
	var err error
	var success bool
	var username string
	var password string
	var accountID uuid.UUID

	accountID = term.AnonymousID

	authHeader := rctx.GetHeader("Authorization")
	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 = term.AnonymousID
	return success, accountID, err
}

func (hand *Handler) ValidatePassword(ctx context.Context, username, password string) (bool, uuid.UUID, error) {
	var err error
	var accountID uuid.UUID
	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 uuid.UUID, reqRight term.Right, subject string) (bool, error) {
	var err error
	var res bool
	//hand.logg.Debugf("Cop check your right %s: %s %s", accountID, reqRight, subject)
	//         =[]=
	//        .------.       ---
	//   .---[-]  [#] \--, ---
	//  >|   [ ]  [ ]    |     ---...
	//   '--0-------0----'
	//  Bad news for you, baby.... #
	//
	exists, grants, err := hand.mdb.ListGrantsByAccoundIDRight(ctx, accountID, reqRight)
	if err != nil {
		return res, err
	}
	if !exists {
		return res, err
	}
	switch reqRight {
	case term.RightReadFiles, term.RightWriteFiles:
		for _, grant := range grants {
			re, err := regexp.Compile(grant.Pattern)
			if err != nil {
				hand.logg.Warningf("Wrong pattern %s for grant %s: %v", grant.Pattern, grant.ID, err)
				err = nil
				continue
			}
			if re.MatchString(subject) {
				res = true
				break
			}
		}
	default:
		// NOP
	}
	res = true
	return res, err
}
