package keybase

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

type NewGrantInput struct {
	ServiceID int64           `json:"service_id"`
	UcanID    int64           `json:"ucan_id,omitempty"`
	Scopes    json.RawMessage `json:"scopes"`
	Accounts  json.RawMessage `json:"accounts"`
	ExpiresAt string          `json:"expires_at,omitempty"`
}

func (am *ActionManager) CreateGrant(ctx context.Context, params NewGrantInput) (*GrantResult, error) {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	if am.kb.didID == 0 {
		return nil, fmt.Errorf("DID not initialized")
	}

	var ucanID *int64
	if params.UcanID != 0 {
		ucanID = &params.UcanID
	}

	var expiresAt *string
	if params.ExpiresAt != "" {
		expiresAt = &params.ExpiresAt
	}

	scopes := params.Scopes
	if scopes == nil {
		scopes = json.RawMessage(`[]`)
	}
	accounts := params.Accounts
	if accounts == nil {
		accounts = json.RawMessage(`[]`)
	}

	g, err := am.kb.queries.CreateGrant(ctx, CreateGrantParams{
		DidID:     am.kb.didID,
		ServiceID: params.ServiceID,
		UcanID:    ucanID,
		Scopes:    scopes,
		Accounts:  accounts,
		ExpiresAt: expiresAt,
	})
	if err != nil {
		return nil, fmt.Errorf("create grant: %w", err)
	}

	svc, err := am.kb.queries.GetServiceByID(ctx, g.ServiceID)
	if err != nil {
		return nil, fmt.Errorf("get service: %w", err)
	}

	serviceLogo := ""
	if svc.LogoUrl != nil {
		serviceLogo = *svc.LogoUrl
	}

	lastUsed := ""
	if g.LastUsed != nil {
		lastUsed = *g.LastUsed
	}

	expires := ""
	if g.ExpiresAt != nil {
		expires = *g.ExpiresAt
	}

	return &GrantResult{
		ID:            g.ID,
		ServiceName:   svc.Name,
		ServiceOrigin: svc.Origin,
		ServiceLogo:   serviceLogo,
		Scopes:        g.Scopes,
		Accounts:      g.Accounts,
		Status:        g.Status,
		GrantedAt:     g.GrantedAt,
		LastUsed:      lastUsed,
		ExpiresAt:     expires,
	}, nil
}

func (am *ActionManager) GetGrantByService(ctx context.Context, serviceID int64) (*GrantResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	if am.kb.didID == 0 {
		return nil, fmt.Errorf("DID not initialized")
	}

	g, err := am.kb.queries.GetGrantByService(ctx, GetGrantByServiceParams{
		DidID:     am.kb.didID,
		ServiceID: serviceID,
	})
	if err != nil {
		return nil, fmt.Errorf("get grant by service: %w", err)
	}

	svc, err := am.kb.queries.GetServiceByID(ctx, g.ServiceID)
	if err != nil {
		return nil, fmt.Errorf("get service: %w", err)
	}

	serviceLogo := ""
	if svc.LogoUrl != nil {
		serviceLogo = *svc.LogoUrl
	}

	lastUsed := ""
	if g.LastUsed != nil {
		lastUsed = *g.LastUsed
	}

	expires := ""
	if g.ExpiresAt != nil {
		expires = *g.ExpiresAt
	}

	return &GrantResult{
		ID:            g.ID,
		ServiceName:   svc.Name,
		ServiceOrigin: svc.Origin,
		ServiceLogo:   serviceLogo,
		Scopes:        g.Scopes,
		Accounts:      g.Accounts,
		Status:        g.Status,
		GrantedAt:     g.GrantedAt,
		LastUsed:      lastUsed,
		ExpiresAt:     expires,
	}, nil
}

func (am *ActionManager) UpdateGrantScopes(ctx context.Context, grantID int64, scopes, accounts json.RawMessage) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	return am.kb.queries.UpdateGrantScopes(ctx, UpdateGrantScopesParams{
		Scopes:   scopes,
		Accounts: accounts,
		ID:       grantID,
	})
}

func (am *ActionManager) UpdateGrantLastUsed(ctx context.Context, grantID int64) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	return am.kb.queries.UpdateGrantLastUsed(ctx, grantID)
}

func (am *ActionManager) SuspendGrant(ctx context.Context, grantID int64) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	return am.kb.queries.SuspendGrant(ctx, grantID)
}

func (am *ActionManager) ReactivateGrant(ctx context.Context, grantID int64) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	return am.kb.queries.ReactivateGrant(ctx, grantID)
}

func (am *ActionManager) CountActiveGrants(ctx context.Context) (int64, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	if am.kb.didID == 0 {
		return 0, nil
	}

	return am.kb.queries.CountActiveGrants(ctx, am.kb.didID)
}
