package inventory

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"

	"github.com/TaruLab/keluargamart-backend/internal/auth"
	"github.com/TaruLab/keluargamart-backend/internal/db"
	"github.com/jackc/pgx/v5"
)

type requestKey struct{}
type movementRequest struct {
	Key, Operation, Fingerprint string
	UserID                      int64
}

// WithMovementRequest binds an HTTP retry identity without adding client keys to business DTOs.
func WithMovementRequest(ctx context.Context, key, operation string, input any) context.Context {
	if key == "" {
		return ctx
	}
	u := auth.UserFrom(ctx)
	if u == nil {
		return ctx
	}
	payload, _ := json.Marshal(input)
	sum := sha256.Sum256(payload)
	return context.WithValue(ctx, requestKey{}, movementRequest{key, operation, hex.EncodeToString(sum[:]), u.ID})
}

// ClaimMovementRequest serializes identical retries on the transaction advisory lock.
// The response is persisted in the SAME transaction as the physical mutation.
func ClaimMovementRequest(ctx context.Context, q db.DBTX) ([]byte, error) {
	req, ok := ctx.Value(requestKey{}).(movementRequest)
	if !ok {
		return nil, nil
	}
	lock := req.Operation + ":" + req.Key
	if _, err := q.Exec(ctx, `SELECT pg_advisory_xact_lock(hashtextextended($1,$2))`, lock, req.UserID); err != nil {
		return nil, err
	}
	var fingerprint string
	var response []byte
	err := q.QueryRow(ctx, `SELECT fingerprint,response FROM stock_operation_receipts WHERE user_id=$1 AND operation=$2 AND idempotency_key=$3`, req.UserID, req.Operation, req.Key).Scan(&fingerprint, &response)
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	if fingerprint != req.Fingerprint {
		return nil, errors.New("idempotency key already used with a different stock operation")
	}
	return response, nil
}

func CompleteMovementRequest(ctx context.Context, q db.DBTX, response any) error {
	req, ok := ctx.Value(requestKey{}).(movementRequest)
	if !ok {
		return nil
	}
	payload, err := json.Marshal(response)
	if err != nil {
		return err
	}
	_, err = q.Exec(ctx, `INSERT INTO stock_operation_receipts(user_id,operation,idempotency_key,fingerprint,response) VALUES($1,$2,$3,$4,$5)`, req.UserID, req.Operation, req.Key, req.Fingerprint, payload)
	return err
}
