package action

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"github.com/pocketbase/pocketbase/core"
	"github.com/pocketbase/pocketbase/tokens"
	"github.com/pocketbase/pocketbase/tools/security"

	"github.com/SecurityBrewery/catalyst/migrations"
	"github.com/SecurityBrewery/catalyst/reaction/action/python"
	"github.com/SecurityBrewery/catalyst/reaction/action/webhook"
)

func Run(ctx context.Context, app core.App, actionName, actionData, payload string) ([]byte, error) {
	action, err := decode(actionName, actionData)
	if err != nil {
		return nil, err
	}

	if a, ok := action.(authenticatedAction); ok {
		token, err := systemToken(app)
		if err != nil {
			return nil, fmt.Errorf("failed to get system token: %w", err)
		}

		a.SetEnv([]string{
			"CATALYST_APP_URL=" + app.Settings().Meta.AppUrl,
			"CATALYST_TOKEN=" + token,
		})
	}

	return action.Run(ctx, payload)
}

type action interface {
	Run(ctx context.Context, payload string) ([]byte, error)
}

type authenticatedAction interface {
	SetEnv(env []string)
}

func decode(actionName, actionData string) (action, error) {
	switch actionName {
	case "python":
		var reaction python.Python
		if err := json.Unmarshal([]byte(actionData), &reaction); err != nil {
			return nil, err
		}

		return &reaction, nil
	case "webhook":
		var reaction webhook.Webhook
		if err := json.Unmarshal([]byte(actionData), &reaction); err != nil {
			return nil, err
		}

		return &reaction, nil
	default:
		return nil, fmt.Errorf("action %q not found", actionName)
	}
}

func systemToken(app core.App) (string, error) {
	authRecord, err := app.Dao().FindAuthRecordByUsername(migrations.UserCollectionName, migrations.SystemUserID)
	if err != nil {
		return "", fmt.Errorf("failed to find system auth record: %w", err)
	}

	return security.NewJWT(
		jwt.MapClaims{
			"id":           authRecord.Id,
			"type":         tokens.TypeAuthRecord,
			"collectionId": authRecord.Collection().Id,
		},
		authRecord.TokenKey()+app.Settings().RecordAuthToken.Secret,
		int64(time.Second*60),
	)
}
