package postgres

import (
	"context"
	"database/sql"

	"github.com/google/uuid"
	"github.com/rwadurian/mpc-system/services/server-party/domain/entities"
	"github.com/rwadurian/mpc-system/services/server-party/domain/repositories"
)

// KeySharePostgresRepo implements KeyShareRepository for PostgreSQL
type KeySharePostgresRepo struct {
	db *sql.DB
}

// NewKeySharePostgresRepo creates a new PostgreSQL key share repository
func NewKeySharePostgresRepo(db *sql.DB) *KeySharePostgresRepo {
	return &KeySharePostgresRepo{db: db}
}

// Save persists a new key share
func (r *KeySharePostgresRepo) Save(ctx context.Context, keyShare *entities.PartyKeyShare) error {
	_, err := r.db.ExecContext(ctx, `
		INSERT INTO party_key_shares (
			id, party_id, party_index, session_id, threshold_n, threshold_t,
			share_data, public_key, created_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
	`,
		keyShare.ID,
		keyShare.PartyID,
		keyShare.PartyIndex,
		keyShare.SessionID,
		keyShare.ThresholdN,
		keyShare.ThresholdT,
		keyShare.ShareData,
		keyShare.PublicKey,
		keyShare.CreatedAt,
	)
	return err
}

// FindByID retrieves a key share by ID
func (r *KeySharePostgresRepo) FindByID(ctx context.Context, id uuid.UUID) (*entities.PartyKeyShare, error) {
	var ks entities.PartyKeyShare
	err := r.db.QueryRowContext(ctx, `
		SELECT id, party_id, party_index, session_id, threshold_n, threshold_t,
			   share_data, public_key, created_at, last_used_at
		FROM party_key_shares WHERE id = $1
	`, id).Scan(
		&ks.ID,
		&ks.PartyID,
		&ks.PartyIndex,
		&ks.SessionID,
		&ks.ThresholdN,
		&ks.ThresholdT,
		&ks.ShareData,
		&ks.PublicKey,
		&ks.CreatedAt,
		&ks.LastUsedAt,
	)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	return &ks, nil
}

// FindBySessionAndParty retrieves a key share by session and party
func (r *KeySharePostgresRepo) FindBySessionAndParty(ctx context.Context, sessionID uuid.UUID, partyID string) (*entities.PartyKeyShare, error) {
	var ks entities.PartyKeyShare
	err := r.db.QueryRowContext(ctx, `
		SELECT id, party_id, party_index, session_id, threshold_n, threshold_t,
			   share_data, public_key, created_at, last_used_at
		FROM party_key_shares WHERE session_id = $1 AND party_id = $2
	`, sessionID, partyID).Scan(
		&ks.ID,
		&ks.PartyID,
		&ks.PartyIndex,
		&ks.SessionID,
		&ks.ThresholdN,
		&ks.ThresholdT,
		&ks.ShareData,
		&ks.PublicKey,
		&ks.CreatedAt,
		&ks.LastUsedAt,
	)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	return &ks, nil
}

// FindByPublicKey retrieves key shares by public key
func (r *KeySharePostgresRepo) FindByPublicKey(ctx context.Context, publicKey []byte) ([]*entities.PartyKeyShare, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT id, party_id, party_index, session_id, threshold_n, threshold_t,
			   share_data, public_key, created_at, last_used_at
		FROM party_key_shares WHERE public_key = $1
		ORDER BY created_at DESC
	`, publicKey)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	return r.scanKeyShares(rows)
}

// Update updates an existing key share
func (r *KeySharePostgresRepo) Update(ctx context.Context, keyShare *entities.PartyKeyShare) error {
	_, err := r.db.ExecContext(ctx, `
		UPDATE party_key_shares SET last_used_at = $1 WHERE id = $2
	`, keyShare.LastUsedAt, keyShare.ID)
	return err
}

// Delete removes a key share
func (r *KeySharePostgresRepo) Delete(ctx context.Context, id uuid.UUID) error {
	_, err := r.db.ExecContext(ctx, `DELETE FROM party_key_shares WHERE id = $1`, id)
	return err
}

// ListByParty lists all key shares for a party
func (r *KeySharePostgresRepo) ListByParty(ctx context.Context, partyID string) ([]*entities.PartyKeyShare, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT id, party_id, party_index, session_id, threshold_n, threshold_t,
			   share_data, public_key, created_at, last_used_at
		FROM party_key_shares WHERE party_id = $1
		ORDER BY created_at DESC
	`, partyID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	return r.scanKeyShares(rows)
}

func (r *KeySharePostgresRepo) scanKeyShares(rows *sql.Rows) ([]*entities.PartyKeyShare, error) {
	var keyShares []*entities.PartyKeyShare
	for rows.Next() {
		var ks entities.PartyKeyShare
		err := rows.Scan(
			&ks.ID,
			&ks.PartyID,
			&ks.PartyIndex,
			&ks.SessionID,
			&ks.ThresholdN,
			&ks.ThresholdT,
			&ks.ShareData,
			&ks.PublicKey,
			&ks.CreatedAt,
			&ks.LastUsedAt,
		)
		if err != nil {
			return nil, err
		}
		keyShares = append(keyShares, &ks)
	}
	return keyShares, rows.Err()
}

// Ensure interface compliance
var _ repositories.KeyShareRepository = (*KeySharePostgresRepo)(nil)
