package keybase

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
)

type KeyShareResult struct {
	ID             int64  `json:"id"`
	ShareID        string `json:"share_id"`
	KeyID          string `json:"key_id"`
	PartyIndex     int64  `json:"party_index"`
	Threshold      int64  `json:"threshold"`
	TotalParties   int64  `json:"total_parties"`
	Curve          string `json:"curve"`
	PublicKey      string `json:"public_key"`
	ChainCode      string `json:"chain_code,omitempty"`
	DerivationPath string `json:"derivation_path,omitempty"`
	Status         string `json:"status"`
	CreatedAt      string `json:"created_at"`
	RotatedAt      string `json:"rotated_at,omitempty"`
}

type NewKeyShareInput struct {
	KeyID          string `json:"key_id"`
	PartyIndex     int64  `json:"party_index"`
	Threshold      int64  `json:"threshold"`
	TotalParties   int64  `json:"total_parties"`
	Curve          string `json:"curve"`
	ShareData      string `json:"share_data"`
	PublicKey      string `json:"public_key"`
	ChainCode      string `json:"chain_code,omitempty"`
	DerivationPath string `json:"derivation_path,omitempty"`
}

func (am *ActionManager) CreateKeyShare(ctx context.Context, params NewKeyShareInput) (*KeyShareResult, error) {
	am.kb.mu.Lock()
	defer am.kb.mu.Unlock()

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

	shareID := generateShareID()

	var chainCode, derivationPath *string
	if params.ChainCode != "" {
		chainCode = &params.ChainCode
	}
	if params.DerivationPath != "" {
		derivationPath = &params.DerivationPath
	}

	ks, err := am.kb.queries.CreateKeyShare(ctx, CreateKeyShareParams{
		DidID:          am.kb.didID,
		ShareID:        shareID,
		KeyID:          params.KeyID,
		PartyIndex:     params.PartyIndex,
		Threshold:      params.Threshold,
		TotalParties:   params.TotalParties,
		Curve:          params.Curve,
		ShareData:      params.ShareData,
		PublicKey:      params.PublicKey,
		ChainCode:      chainCode,
		DerivationPath: derivationPath,
	})
	if err != nil {
		return nil, fmt.Errorf("create key share: %w", err)
	}

	return keyShareToResult(&ks), nil
}

func (am *ActionManager) ListKeyShares(ctx context.Context) ([]KeyShareResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

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

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

	results := make([]KeyShareResult, len(shares))
	for i, ks := range shares {
		results[i] = *keyShareToResult(&ks)
	}

	return results, nil
}

func (am *ActionManager) GetKeyShareByID(ctx context.Context, shareID string) (*KeyShareResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

	ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID)
	if err != nil {
		return nil, fmt.Errorf("get key share: %w", err)
	}

	return keyShareToResult(&ks), nil
}

func (am *ActionManager) GetKeyShareByKeyID(ctx context.Context, keyID string) (*KeyShareResult, error) {
	am.kb.mu.RLock()
	defer am.kb.mu.RUnlock()

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

	ks, err := am.kb.queries.GetKeyShareByKeyID(ctx, GetKeyShareByKeyIDParams{
		DidID: am.kb.didID,
		KeyID: keyID,
	})
	if err != nil {
		return nil, fmt.Errorf("get key share by key ID: %w", err)
	}

	return keyShareToResult(&ks), nil
}

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

	ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID)
	if err != nil {
		return fmt.Errorf("get key share: %w", err)
	}

	return am.kb.queries.RotateKeyShare(ctx, ks.ID)
}

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

	ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID)
	if err != nil {
		return fmt.Errorf("get key share: %w", err)
	}

	return am.kb.queries.ArchiveKeyShare(ctx, ks.ID)
}

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

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

	ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID)
	if err != nil {
		return fmt.Errorf("get key share: %w", err)
	}

	return am.kb.queries.DeleteKeyShare(ctx, DeleteKeyShareParams{
		ID:    ks.ID,
		DidID: am.kb.didID,
	})
}

func generateShareID() string {
	b := make([]byte, 16)
	rand.Read(b)
	return "ks_" + hex.EncodeToString(b)
}

func keyShareToResult(ks *KeyShare) *KeyShareResult {
	chainCode := ""
	if ks.ChainCode != nil {
		chainCode = *ks.ChainCode
	}
	derivationPath := ""
	if ks.DerivationPath != nil {
		derivationPath = *ks.DerivationPath
	}
	rotatedAt := ""
	if ks.RotatedAt != nil {
		rotatedAt = *ks.RotatedAt
	}

	return &KeyShareResult{
		ID:             ks.ID,
		ShareID:        ks.ShareID,
		KeyID:          ks.KeyID,
		PartyIndex:     ks.PartyIndex,
		Threshold:      ks.Threshold,
		TotalParties:   ks.TotalParties,
		Curve:          ks.Curve,
		PublicKey:      ks.PublicKey,
		ChainCode:      chainCode,
		DerivationPath: derivationPath,
		Status:         ks.Status,
		CreatedAt:      ks.CreatedAt,
		RotatedAt:      rotatedAt,
	}
}
