package keybase

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

type NewCredentialInput struct {
	CredentialID   string   `json:"credential_id"`
	PublicKey      string   `json:"public_key"`
	PublicKeyAlg   int64    `json:"public_key_alg"`
	AAGUID         string   `json:"aaguid,omitempty"`
	Transports     []string `json:"transports"`
	DeviceName     string   `json:"device_name"`
	DeviceType     string   `json:"device_type"`
	Authenticator  string   `json:"authenticator,omitempty"`
	IsDiscoverable bool     `json:"is_discoverable"`
	BackedUp       bool     `json:"backed_up"`
}

func (am *ActionManager) CreateCredential(ctx context.Context, params NewCredentialInput) (*CredentialResult, error) {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

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

	var aaguid, authenticator *string
	if params.AAGUID != "" {
		aaguid = &params.AAGUID
	}
	if params.Authenticator != "" {
		authenticator = &params.Authenticator
	}

	transports, err := json.Marshal(params.Transports)
	if err != nil {
		return nil, fmt.Errorf("marshal transports: %w", err)
	}

	var isDiscoverable, backedUp int64
	if params.IsDiscoverable {
		isDiscoverable = 1
	}
	if params.BackedUp {
		backedUp = 1
	}

	cred, err := am.kb.queries.CreateCredential(ctx, CreateCredentialParams{
		DidID:          am.kb.didID,
		CredentialID:   params.CredentialID,
		PublicKey:      params.PublicKey,
		PublicKeyAlg:   params.PublicKeyAlg,
		Aaguid:         aaguid,
		Transports:     transports,
		DeviceName:     params.DeviceName,
		DeviceType:     params.DeviceType,
		Authenticator:  authenticator,
		IsDiscoverable: isDiscoverable,
		BackedUp:       backedUp,
	})
	if err != nil {
		return nil, fmt.Errorf("create credential: %w", err)
	}

	return credentialToResult(&cred), nil
}

func (am *ActionManager) UpdateCredentialCounter(ctx context.Context, credentialID string, signCount int64) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	cred, err := am.kb.queries.GetCredentialByID(ctx, credentialID)
	if err != nil {
		return fmt.Errorf("get credential: %w", err)
	}

	return am.kb.queries.UpdateCredentialCounter(ctx, UpdateCredentialCounterParams{
		SignCount: signCount,
		ID:        cred.ID,
	})
}

func (am *ActionManager) RenameCredential(ctx context.Context, credentialID string, newName string) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

	cred, err := am.kb.queries.GetCredentialByID(ctx, credentialID)
	if err != nil {
		return fmt.Errorf("get credential: %w", err)
	}

	return am.kb.queries.RenameCredential(ctx, RenameCredentialParams{
		DeviceName: newName,
		ID:         cred.ID,
	})
}

func (am *ActionManager) DeleteCredential(ctx context.Context, credentialID string) error {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

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

	cred, err := am.kb.queries.GetCredentialByID(ctx, credentialID)
	if err != nil {
		return fmt.Errorf("get credential: %w", err)
	}

	return am.kb.queries.DeleteCredential(ctx, DeleteCredentialParams{
		ID:    cred.ID,
		DidID: am.kb.didID,
	})
}

func (am *ActionManager) CountCredentialsByDID(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.CountCredentialsByDID(ctx, am.kb.didID)
}

func credentialToResult(cred *Credential) *CredentialResult {
	var transports []string
	if err := json.Unmarshal(cred.Transports, &transports); err != nil {
		transports = []string{}
	}

	authenticator := ""
	if cred.Authenticator != nil {
		authenticator = *cred.Authenticator
	}

	return &CredentialResult{
		ID:             cred.ID,
		CredentialID:   cred.CredentialID,
		DeviceName:     cred.DeviceName,
		DeviceType:     cred.DeviceType,
		Authenticator:  authenticator,
		Transports:     transports,
		SignCount:      cred.SignCount,
		IsDiscoverable: cred.IsDiscoverable == 1,
		BackedUp:       cred.BackedUp == 1,
		CreatedAt:      cred.CreatedAt,
		LastUsed:       cred.LastUsed,
	}
}
