package keybase

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

type HandlerFunc func(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error)

type resourceHandlers map[string]HandlerFunc

var handlers = map[string]resourceHandlers{
	"accounts": {
		"list": handleAccountList,
		"get":  handleAccountGet,
	},
	"credentials": {
		"list": handleCredentialList,
		"get":  handleCredentialGet,
	},
	"sessions": {
		"list":   handleSessionList,
		"revoke": handleSessionRevoke,
	},
	"grants": {
		"list":   handleGrantList,
		"revoke": handleGrantRevoke,
	},
	"enclaves": {
		"list":    handleEnclaveList,
		"get":     handleEnclaveGet,
		"rotate":  handleEnclaveRotate,
		"archive": handleEnclaveArchive,
		"delete":  handleEnclaveDelete,
	},
	"delegations": {
		"list":          handleDelegationList,
		"list_received": handleDelegationListReceived,
		"list_command":  handleDelegationListCommand,
		"get":           handleDelegationGet,
		"revoke":        handleDelegationRevoke,
		"verify":        handleDelegationVerify,
		"cleanup":       handleDelegationCleanup,
	},
	"ucans": {
		"list":    handleDelegationList,
		"get":     handleDelegationGet,
		"revoke":  handleDelegationRevoke,
		"verify":  handleDelegationVerify,
		"cleanup": handleDelegationCleanup,
	},
	"verification_methods": {
		"list":   handleVerificationMethodList,
		"get":    handleVerificationMethodGet,
		"delete": handleVerificationMethodDelete,
	},
	"services": {
		"list":      handleServiceList,
		"get":       handleServiceGet,
		"get_by_id": handleServiceGetByID,
	},
}

func Exec(ctx context.Context, resource, action, subject string) (json.RawMessage, error) {
	am, err := NewActionManager()
	if err != nil {
		return nil, fmt.Errorf("action manager: %w", err)
	}

	resHandlers, ok := handlers[resource]
	if !ok {
		return nil, fmt.Errorf("unknown resource: %s", resource)
	}

	handler, ok := resHandlers[action]
	if !ok {
		return nil, fmt.Errorf("unknown action for %s: %s", resource, action)
	}

	return handler(ctx, am, subject)
}

func handleAccountList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	accounts, err := am.ListAccounts(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(accounts)
}

func handleAccountGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (address) required for get action")
	}
	account, err := am.GetAccountByAddress(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(account)
}

func handleCredentialList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	credentials, err := am.ListCredentials(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(credentials)
}

func handleCredentialGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (credential_id) required for get action")
	}
	credential, err := am.GetCredentialByID(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(credential)
}

func handleSessionList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	sessions, err := am.ListSessions(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(sessions)
}

func handleSessionRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (session_id) required for revoke action")
	}
	if err := am.RevokeSession(ctx, subject); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"revoked": true})
}

func handleGrantList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	grants, err := am.ListGrants(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(grants)
}

func handleGrantRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (grant_id) required for revoke action")
	}
	var grantID int64
	if _, err := fmt.Sscanf(subject, "%d", &grantID); err != nil {
		return nil, fmt.Errorf("invalid grant_id: %w", err)
	}
	if err := am.RevokeGrant(ctx, grantID); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"revoked": true})
}

func handleEnclaveList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	enclaves, err := am.ListEnclaves(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(enclaves)
}

func handleEnclaveGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (enclave_id) required for get action")
	}
	enc, err := am.GetEnclaveByID(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(enc)
}

func handleEnclaveRotate(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (enclave_id) required for rotate action")
	}
	if err := am.RotateEnclave(ctx, subject); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"rotated": true})
}

func handleEnclaveArchive(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (enclave_id) required for archive action")
	}
	if err := am.ArchiveEnclave(ctx, subject); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"archived": true})
}

func handleEnclaveDelete(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (enclave_id) required for delete action")
	}
	if err := am.DeleteEnclave(ctx, subject); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"deleted": true})
}

func handleDelegationList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	delegations, err := am.ListDelegations(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(delegations)
}

func handleDelegationListReceived(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (audience DID) required for list_received action")
	}
	delegations, err := am.ListDelegationsByAudience(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(delegations)
}

func handleDelegationListCommand(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (command) required for list_command action")
	}
	delegations, err := am.ListDelegationsForCommand(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(delegations)
}

func handleDelegationGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (cid) required for get action")
	}
	delegation, err := am.GetDelegationByCID(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(delegation)
}

func handleDelegationRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (cid) required for revoke action")
	}
	kb := Get()
	revokedBy := ""
	if kb != nil {
		revokedBy = kb.DID()
	}
	if err := am.RevokeDelegation(ctx, RevokeDelegationParams{
		DelegationCID: subject,
		RevokedBy:     revokedBy,
		Reason:        "user revoked",
	}); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"revoked": true})
}

func handleDelegationVerify(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (cid) required for verify action")
	}
	revoked, err := am.IsDelegationRevoked(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"valid": !revoked, "revoked": revoked})
}

func handleDelegationCleanup(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	if err := am.CleanExpiredDelegations(ctx); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"cleaned": true})
}

func handleVerificationMethodList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	vms, err := am.ListVerificationMethodsFull(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(vms)
}

func handleVerificationMethodGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (method_id) required for get action")
	}
	vm, err := am.GetVerificationMethod(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(vm)
}

func handleVerificationMethodDelete(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (method_id) required for delete action")
	}
	if err := am.DeleteVerificationMethod(ctx, subject); err != nil {
		return nil, err
	}
	return json.Marshal(map[string]bool{"deleted": true})
}

func handleServiceList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
	services, err := am.ListVerifiedServices(ctx)
	if err != nil {
		return nil, err
	}
	return json.Marshal(services)
}

func handleServiceGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (origin) required for get action")
	}
	svc, err := am.GetServiceByOrigin(ctx, subject)
	if err != nil {
		return nil, err
	}
	return json.Marshal(svc)
}

func handleServiceGetByID(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
	if subject == "" {
		return nil, errors.New("subject (service_id) required for get_by_id action")
	}
	var serviceID int64
	if _, err := fmt.Sscanf(subject, "%d", &serviceID); err != nil {
		return nil, fmt.Errorf("invalid service_id: %w", err)
	}
	svc, err := am.GetServiceByID(ctx, serviceID)
	if err != nil {
		return nil, err
	}
	return json.Marshal(svc)
}
