package keybase

import (
	"context"
	"fmt"
)

// =============================================================================
// DELEGATION ACTIONS (UCAN v1.0.0-rc.1)
// =============================================================================

// DelegationResult represents a delegation in API responses.
type DelegationResult struct {
	ID          int64  `json:"id"`
	CID         string `json:"cid"`
	Issuer      string `json:"iss"`
	Audience    string `json:"aud"`
	Subject     string `json:"sub,omitempty"`
	Command     string `json:"cmd"`
	Policy      string `json:"pol,omitempty"`
	NotBefore   string `json:"nbf,omitempty"`
	Expiration  string `json:"exp,omitempty"`
	IsRoot      bool   `json:"is_root"`
	IsPowerline bool   `json:"is_powerline"`
	CreatedAt   string `json:"created_at"`
}

// StoreDelegationParams contains parameters for storing a delegation.
type StoreDelegationParams struct {
	CID         string `json:"cid"`
	Envelope    []byte `json:"envelope"`
	Issuer      string `json:"iss"`
	Audience    string `json:"aud"`
	Subject     string `json:"sub,omitempty"`
	Command     string `json:"cmd"`
	Policy      string `json:"pol,omitempty"`
	NotBefore   string `json:"nbf,omitempty"`
	Expiration  string `json:"exp,omitempty"`
	IsRoot      bool   `json:"is_root"`
	IsPowerline bool   `json:"is_powerline"`
}

// StoreDelegation stores a new UCAN delegation envelope.
func (am *ActionManager) StoreDelegation(ctx context.Context, params StoreDelegationParams) (*DelegationResult, error) {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

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

	var sub, pol, nbf, exp *string
	if params.Subject != "" {
		sub = &params.Subject
	}
	if params.Policy != "" {
		pol = &params.Policy
	}
	if params.NotBefore != "" {
		nbf = &params.NotBefore
	}
	if params.Expiration != "" {
		exp = &params.Expiration
	}

	isRoot := int64(0)
	if params.IsRoot {
		isRoot = 1
	}
	isPowerline := int64(0)
	if params.IsPowerline {
		isPowerline = 1
	}

	d, err := am.kb.queries.CreateDelegation(ctx, CreateDelegationParams{
		DidID:       am.kb.didID,
		Cid:         params.CID,
		Envelope:    params.Envelope,
		Iss:         params.Issuer,
		Aud:         params.Audience,
		Sub:         sub,
		Cmd:         params.Command,
		Pol:         pol,
		Nbf:         nbf,
		Exp:         exp,
		IsRoot:      isRoot,
		IsPowerline: isPowerline,
	})
	if err != nil {
		return nil, fmt.Errorf("create delegation: %w", err)
	}

	return delegationToResult(d), nil
}

// GetDelegationByCID retrieves a delegation by its CID.
func (am *ActionManager) GetDelegationByCID(ctx context.Context, cid string) (*DelegationResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	d, err := am.kb.queries.GetDelegationByCID(ctx, cid)
	if err != nil {
		return nil, fmt.Errorf("get delegation: %w", err)
	}

	return delegationToResult(d), nil
}

// GetDelegationEnvelope retrieves the raw CBOR envelope for a delegation.
func (am *ActionManager) GetDelegationEnvelope(ctx context.Context, cid string) ([]byte, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	envelope, err := am.kb.queries.GetDelegationEnvelopeByCID(ctx, cid)
	if err != nil {
		return nil, fmt.Errorf("get delegation envelope: %w", err)
	}

	return envelope, nil
}

// ListDelegations returns all active delegations for the current DID.
func (am *ActionManager) ListDelegations(ctx context.Context) ([]DelegationResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

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

	delegations, err := am.kb.queries.ListDelegationsByDID(ctx, am.kb.didID)
	if err != nil {
		return nil, fmt.Errorf("list delegations: %w", err)
	}

	results := make([]DelegationResult, len(delegations))
	for i, d := range delegations {
		results[i] = *delegationToResult(d)
	}

	return results, nil
}

// ListDelegationsByIssuer returns delegations issued by a specific DID.
func (am *ActionManager) ListDelegationsByIssuer(ctx context.Context, issuer string) ([]DelegationResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	delegations, err := am.kb.queries.ListDelegationsByIssuer(ctx, issuer)
	if err != nil {
		return nil, fmt.Errorf("list delegations by issuer: %w", err)
	}

	results := make([]DelegationResult, len(delegations))
	for i, d := range delegations {
		results[i] = *delegationToResult(d)
	}

	return results, nil
}

// ListDelegationsByAudience returns delegations granted to a specific DID.
func (am *ActionManager) ListDelegationsByAudience(ctx context.Context, audience string) ([]DelegationResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	delegations, err := am.kb.queries.ListDelegationsByAudience(ctx, audience)
	if err != nil {
		return nil, fmt.Errorf("list delegations by audience: %w", err)
	}

	results := make([]DelegationResult, len(delegations))
	for i, d := range delegations {
		results[i] = *delegationToResult(d)
	}

	return results, nil
}

// ListDelegationsForCommand returns delegations that grant a specific command.
func (am *ActionManager) ListDelegationsForCommand(ctx context.Context, cmd string) ([]DelegationResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

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

	delegations, err := am.kb.queries.ListDelegationsForCommand(ctx, ListDelegationsForCommandParams{
		DidID: am.kb.didID,
		Cmd:   cmd,
		Cmd_2: cmd,
	})
	if err != nil {
		return nil, fmt.Errorf("list delegations for command: %w", err)
	}

	results := make([]DelegationResult, len(delegations))
	for i, d := range delegations {
		results[i] = *delegationToResult(d)
	}

	return results, nil
}

// IsDelegationRevoked checks if a delegation has been revoked.
func (am *ActionManager) IsDelegationRevoked(ctx context.Context, cid string) (bool, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	revoked, err := am.kb.queries.IsDelegationRevoked(ctx, cid)
	if err != nil {
		return false, fmt.Errorf("check revocation: %w", err)
	}

	return revoked == 1, nil
}

// RevokeDelegationParams contains parameters for revoking a delegation.
type RevokeDelegationParams struct {
	DelegationCID string `json:"delegation_cid"`
	RevokedBy     string `json:"revoked_by"`
	InvocationCID string `json:"invocation_cid,omitempty"`
	Reason        string `json:"reason,omitempty"`
}

// RevokeDelegation revokes a delegation by creating a revocation record.
func (am *ActionManager) RevokeDelegation(ctx context.Context, params RevokeDelegationParams) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	var invocationCID, reason *string
	if params.InvocationCID != "" {
		invocationCID = &params.InvocationCID
	}
	if params.Reason != "" {
		reason = &params.Reason
	}

	err := am.kb.queries.CreateRevocation(ctx, CreateRevocationParams{
		DelegationCid: params.DelegationCID,
		RevokedBy:     params.RevokedBy,
		InvocationCid: invocationCID,
		Reason:        reason,
	})
	if err != nil {
		return fmt.Errorf("create revocation: %w", err)
	}

	return nil
}

// DeleteDelegation deletes a delegation from the database.
func (am *ActionManager) DeleteDelegation(ctx context.Context, cid string) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

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

	err := am.kb.queries.DeleteDelegation(ctx, DeleteDelegationParams{
		Cid:   cid,
		DidID: am.kb.didID,
	})
	if err != nil {
		return fmt.Errorf("delete delegation: %w", err)
	}

	return nil
}

// CleanExpiredDelegations removes delegations expired more than 30 days ago.
func (am *ActionManager) CleanExpiredDelegations(ctx context.Context) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	if err := am.kb.queries.CleanExpiredDelegations(ctx); err != nil {
		return fmt.Errorf("clean expired delegations: %w", err)
	}

	return nil
}

// delegationToResult converts a UcanDelegation to DelegationResult.
func delegationToResult(d UcanDelegation) *DelegationResult {
	subject := ""
	if d.Sub != nil {
		subject = *d.Sub
	}
	policy := ""
	if d.Pol != nil {
		policy = *d.Pol
	}
	notBefore := ""
	if d.Nbf != nil {
		notBefore = *d.Nbf
	}
	expiration := ""
	if d.Exp != nil {
		expiration = *d.Exp
	}

	return &DelegationResult{
		ID:          d.ID,
		CID:         d.Cid,
		Issuer:      d.Iss,
		Audience:    d.Aud,
		Subject:     subject,
		Command:     d.Cmd,
		Policy:      policy,
		NotBefore:   notBefore,
		Expiration:  expiration,
		IsRoot:      d.IsRoot == 1,
		IsPowerline: d.IsPowerline == 1,
		CreatedAt:   d.CreatedAt,
	}
}
