package app

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/app/capabilities"
	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/app/runtime"
)

func (store *Store) IssueAppCapabilityToken(ctx context.Context, installationID string, requestID string, scopes []string, ttl time.Duration) (string, error) {
	installation, err := runtime.LoadInstallationAuthRecord(ctx, store.db, installationID)
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			return "", ErrForbidden
		}
		return "", err
	}
	if !installation.Enabled {
		return "", ErrForbidden
	}

	requestedScopes := runtime.NormalizeCapabilityScopes(scopes)
	if len(requestedScopes) == 0 {
		requestedScopes = append(requestedScopes, installation.GrantedScopes...)
	}
	if !runtime.CapabilityScopeSetCovers(installation.GrantedScopes, requestedScopes) {
		return "", ErrForbidden
	}

	now := time.Now().UTC()
	tokenID, err := identifiers.NewEntityID("tok")
	if err != nil {
		return "", fmt.Errorf("generate app capability token id: %w", err)
	}

	normalizedTTL := ttl
	switch {
	case normalizedTTL <= 0:
		normalizedTTL = defaultAppCapabilityTTL
	case normalizedTTL > maxAppCapabilityTTL:
		normalizedTTL = maxAppCapabilityTTL
	}

	return store.appCapabilitySigner.Sign(capabilities.Claims{
		Subject:       installation.InstallationID,
		AppID:         installation.AppID,
		WorkspaceID:   installation.WorkspaceID,
		MemberID:      installation.MemberID,
		Scopes:        requestedScopes,
		TokenID:       tokenID,
		RequestID:     strings.TrimSpace(requestID),
		IssuedAtUnix:  now.Unix(),
		ExpiresAtUnix: now.Add(normalizedTTL).Unix(),
	})
}

func (store *Store) AuthenticateAppInstallation(ctx context.Context, installationID string, bearerToken string, requestedScopes []string) (runtime.AuthenticatedInstallation, error) {
	claims, err := store.appCapabilitySigner.Verify(strings.TrimSpace(bearerToken))
	if err != nil {
		if errors.Is(err, capabilities.ErrInvalidToken) || errors.Is(err, capabilities.ErrExpiredToken) {
			return runtime.AuthenticatedInstallation{}, ErrUnauthorized
		}
		return runtime.AuthenticatedInstallation{}, fmt.Errorf("verify app capability token: %w", err)
	}

	installation, err := runtime.LoadInstallationAuthRecord(ctx, store.db, installationID)
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			return runtime.AuthenticatedInstallation{}, ErrUnauthorized
		}
		return runtime.AuthenticatedInstallation{}, err
	}
	if !installation.Enabled {
		return runtime.AuthenticatedInstallation{}, ErrUnauthorized
	}

	trimmedInstallationID := strings.TrimSpace(installationID)
	if strings.TrimSpace(claims.Subject) != trimmedInstallationID ||
		strings.TrimSpace(claims.AppID) != installation.AppID ||
		strings.TrimSpace(claims.WorkspaceID) != installation.WorkspaceID ||
		strings.TrimSpace(claims.MemberID) != installation.MemberID {
		return runtime.AuthenticatedInstallation{}, ErrUnauthorized
	}

	normalizedRequestedScopes := runtime.NormalizeCapabilityScopes(requestedScopes)
	normalizedTokenScopes := runtime.NormalizeCapabilityScopes(claims.Scopes)
	if len(normalizedRequestedScopes) > 0 && !runtime.CapabilityScopeSetCovers(normalizedTokenScopes, normalizedRequestedScopes) {
		return runtime.AuthenticatedInstallation{}, ErrForbidden
	}

	if len(normalizedRequestedScopes) > 0 {
		if _, err := access.DecideServiceInstallationAuthorization(
			access.ServiceInstallationGrant{
				PrincipalID:    installation.AppID,
				WorkspaceID:    installation.WorkspaceID,
				InstallationID: installation.InstallationID,
				MemberID:       installation.MemberID,
				Scopes:         installation.GrantedScopes,
			},
			access.NewServiceInstallationInvokeRequest(
				installation.AppID,
				installation.WorkspaceID,
				installation.InstallationID,
				installation.MemberID,
				normalizedRequestedScopes,
			),
		); err != nil {
			if errors.Is(err, access.ErrForbidden) {
				return runtime.AuthenticatedInstallation{}, ErrForbidden
			}
			return runtime.AuthenticatedInstallation{}, fmt.Errorf("authorize app installation invoke: %w", err)
		}
	}

	return runtime.AuthenticatedInstallation{
		InstallationID: installation.InstallationID,
		AppID:          installation.AppID,
		AppSlug:        installation.AppSlug,
		AppName:        installation.AppName,
		WorkspaceID:    installation.WorkspaceID,
		MemberID:       installation.MemberID,
		Handle:         installation.Handle,
		RequestID:      strings.TrimSpace(claims.RequestID),
		TokenScopes:    normalizedTokenScopes,
		GrantedScopes:  append([]string(nil), installation.GrantedScopes...),
	}, nil
}
