package crypto

import (
	"go.uber.org/zap"
	rbls48581 "source.quilibrium.com/quilibrium/monorepo/bls48581"
	"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
)

type KZGInclusionProver struct {
	prover *kzg.KZGProver
	logger *zap.Logger
}

func NewKZGInclusionProver(logger *zap.Logger) *KZGInclusionProver {
	return &KZGInclusionProver{
		prover: kzg.DefaultKZGProver(),
		logger: logger,
	}
}

// // Commit implements InclusionProver.
// func (k *KZGInclusionProver) Commit(
// 	data []byte,
// 	typeUrl string,
// ) (*InclusionCommitment, error) {
// 	if typeUrl == protobufs.IntrinsicExecutionOutputType {
// 		digest := sha3.NewShake256()
// 		_, err := digest.Write(data)
// 		if err != nil {
// 			k.logger.Error(
// 				"error converting key bundle to polynomial",
// 				zap.Error(err),
// 			)
// 			return nil, errors.Wrap(err, "prove aggregate")
// 		}

// 		expand := make([]byte, 1024)
// 		_, err = digest.Read(expand)
// 		if err != nil {
// 			k.logger.Error(
// 				"error converting key bundle to polynomial",
// 				zap.Error(err),
// 			)
// 			return nil, errors.Wrap(err, "prove aggregate")
// 		}

// 		poly, err := k.prover.BytesToPolynomial(expand)
// 		if err != nil {
// 			return nil, errors.Wrap(err, "commit")
// 		}

// 		k.logger.Debug("proving execution output for inclusion")
// 		polys, err := kzg.FFT(
// 			poly,
// 			*curves.BLS48581(
// 				curves.BLS48581G1().NewGeneratorPoint(),
// 			),
// 			16,
// 			false,
// 		)
// 		if err != nil {
// 			return nil, errors.Wrap(err, "prove")
// 		}

// 		k.logger.Debug("converted execution output chunk to evaluation form")

// 		k.logger.Debug("creating kzg commitment")
// 		points, err := k.prover.Commit(polys)
// 		if err != nil {
// 			return nil, errors.Wrap(err, "prove")
// 		}

// 		return &InclusionCommitment{
// 			TypeUrl:    typeUrl,
// 			Data:       data,
// 			Commitment: points.ToAffineCompressed(),
// 		}, nil
// 	}

// 	poly, err := k.prover.BytesToPolynomial(data)
// 	if err != nil {
// 		return nil, errors.Wrap(err, "commit")
// 	}

// 	points, err := k.prover.Commit(poly)
// 	if err != nil {
// 		return nil, errors.Wrap(err, "commit")
// 	}

// 	return &InclusionCommitment{
// 		TypeUrl:    typeUrl,
// 		Data:       data,
// 		Commitment: points.ToAffineCompressed(),
// 	}, nil
// }

// // ProveAggregate implements InclusionProver.
// func (k *KZGInclusionProver) ProveAggregate(
// 	commits []*InclusionCommitment,
// ) (*InclusionAggregateProof, error) {
// 	polys := [][]curves.PairingScalar{}
// 	commitPoints := []curves.PairingPoint{}
// 	for _, commit := range commits {
// 		switch commit.TypeUrl {
// 		case protobufs.IntrinsicExecutionOutputType:
// 			k.logger.Debug("confirming inclusion in aggregate")
// 			digest := sha3.NewShake256()
// 			_, err := digest.Write(commit.Data)
// 			if err != nil {
// 				k.logger.Error(
// 					"error converting key bundle to polynomial",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}

// 			expand := make([]byte, 1024)
// 			_, err = digest.Read(expand)
// 			if err != nil {
// 				k.logger.Error(
// 					"error converting key bundle to polynomial",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}

// 			poly, err := k.prover.BytesToPolynomial(expand)
// 			if err != nil {
// 				k.logger.Error(
// 					"error converting key bundle to polynomial",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}

// 			evalPoly, err := kzg.FFT(
// 				poly,
// 				*curves.BLS48581(
// 					curves.BLS48581G1().NewGeneratorPoint(),
// 				),
// 				16,
// 				false,
// 			)
// 			if err != nil {
// 				k.logger.Error(
// 					"error performing fast fourier transform on key bundle",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}
// 			k.logger.Debug(
// 				"created fft of polynomial",
// 				zap.Int("poly_size", len(evalPoly)),
// 			)

// 			polys = append(polys, evalPoly)

// 			c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
// 				commit.Commitment,
// 			)
// 			if err != nil {
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}
// 			commitPoints = append(commitPoints, c.(curves.PairingPoint))
// 		default:
// 			k.logger.Debug("confirming inclusion in aggregate")
// 			poly, err := k.prover.BytesToPolynomial(commit.Data)
// 			if err != nil {
// 				k.logger.Error(
// 					"error converting key bundle to polynomial",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}

// 			for i := 0; i < 1024-len(poly); i++ {
// 				poly = append(
// 					poly,
// 					curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
// 				)
// 			}

// 			evalPoly, err := kzg.FFT(
// 				poly,
// 				*curves.BLS48581(
// 					curves.BLS48581G1().NewGeneratorPoint(),
// 				),
// 				1024,
// 				false,
// 			)
// 			if err != nil {
// 				k.logger.Error(
// 					"error performing fast fourier transform on key bundle",
// 					zap.Error(err),
// 				)
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}
// 			k.logger.Debug(
// 				"created fft of polynomial",
// 				zap.Int("poly_size", len(evalPoly)),
// 			)

// 			polys = append(polys, evalPoly)

// 			c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
// 				commit.Commitment,
// 			)
// 			if err != nil {
// 				k.logger.Error("could not verify clock frame", zap.Error(err))
// 				return nil, errors.Wrap(err, "prove aggregate")
// 			}
// 			commitPoints = append(commitPoints, c.(curves.PairingPoint))
// 		}
// 	}

// 	proof, commitment, err := k.prover.ProveAggregate(
// 		polys,
// 		commitPoints,
// 	)
// 	if err != nil {
// 		return nil, errors.Wrap(err, "prove aggregate")
// 	}

// 	if proof.IsIdentity() {
// 		return nil, errors.Wrap(errors.New("invalid proof"), "prove aggregate")
// 	}

// 	return &InclusionAggregateProof{
// 		InclusionCommitments: commits,
// 		AggregateCommitment:  commitment.ToAffineCompressed(),
// 		Proof:                proof.ToAffineCompressed(),
// 	}, nil
// }

// // VerifyAggregate implements InclusionProver.
// func (k *KZGInclusionProver) VerifyAggregate(
// 	proof *InclusionAggregateProof,
// ) (bool, error) {
// 	polys := [][]curves.PairingScalar{}
// 	commitPoints := []curves.PairingPoint{}
// 	for _, commit := range proof.InclusionCommitments {
// 		poly, err := k.prover.BytesToPolynomial(commit.Data)
// 		if err != nil {
// 			return false, errors.Wrap(err, "verify aggregate")
// 		}

// 		polys = append(polys, poly)

// 		point, err := curves.BLS48581G1().Point.FromAffineCompressed(
// 			commit.Commitment,
// 		)
// 		if err != nil {
// 			return false, errors.Wrap(err, "verify aggregate")
// 		}

// 		commitPoints = append(commitPoints, point.(curves.PairingPoint))
// 	}

// 	aggregate, err := curves.BLS48581G1().Point.FromAffineCompressed(
// 		proof.AggregateCommitment,
// 	)
// 	if err != nil {
// 		return false, errors.Wrap(err, "verify aggregate")
// 	}

// 	proofPoint, err := curves.BLS48581G1().Point.FromAffineCompressed(
// 		proof.Proof,
// 	)
// 	if err != nil {
// 		return false, errors.Wrap(err, "verify aggregate")
// 	}

// 	verify, err := k.prover.VerifyAggregateProof(
// 		polys,
// 		commitPoints,
// 		aggregate.(curves.PairingPoint),
// 		proofPoint.(curves.PairingPoint),
// 	)
// 	return verify, errors.Wrap(err, "verify aggregate")
// }

// func (k *KZGInclusionProver) VerifyFrame(
// 	frame *protobufs.ClockFrame,
// ) error {
// 	aggregateCommitments := []curves.PairingPoint{}
// 	for i := 0; i < (len(frame.Input)-516)/74; i++ {
// 		c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
// 			frame.Input[516+(i*74) : 516+(i*74)+74],
// 		)
// 		if err != nil {
// 			k.logger.Error("could not verify clock frame", zap.Error(err))
// 			return errors.Wrap(err, "verify frame")
// 		}
// 		aggregateCommitments = append(aggregateCommitments, c.(curves.PairingPoint))
// 	}

// 	if len(aggregateCommitments) != len(frame.AggregateProofs) {
// 		k.logger.Error(
// 			"commit length mismatched proof for frame",
// 			zap.Int("commit_length", len(aggregateCommitments)),
// 			zap.Int("proof_length", len(frame.AggregateProofs)),
// 		)
// 		return errors.Wrap(
// 			errors.New("commit length mismatched proof for frame"),
// 			"verify frame",
// 		)
// 	}

// 	for i, proof := range frame.AggregateProofs {
// 		aggregatePoly := [][]curves.PairingScalar{}
// 		commitments := []curves.PairingPoint{}

// 		for _, commit := range proof.GetInclusionCommitments() {
// 			switch commit.TypeUrl {
// 			case protobufs.IntrinsicExecutionOutputType:
// 				k.logger.Debug("confirming inclusion in aggregate")
// 				digest := sha3.NewShake256()
// 				_, err := digest.Write(commit.Data)
// 				if err != nil {
// 					k.logger.Error(
// 						"error converting key bundle to polynomial",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}

// 				expand := make([]byte, 1024)
// 				_, err = digest.Read(expand)
// 				if err != nil {
// 					k.logger.Error(
// 						"error converting key bundle to polynomial",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}

// 				poly, err := k.prover.BytesToPolynomial(expand)
// 				if err != nil {
// 					k.logger.Error(
// 						"error converting key bundle to polynomial",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}

// 				evalPoly, err := kzg.FFT(
// 					poly,
// 					*curves.BLS48581(
// 						curves.BLS48581G1().NewGeneratorPoint(),
// 					),
// 					16,
// 					false,
// 				)
// 				if err != nil {
// 					k.logger.Error(
// 						"error performing fast fourier transform on key bundle",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}
// 				k.logger.Debug(
// 					"created fft of polynomial",
// 					zap.Int("poly_size", len(evalPoly)),
// 				)

// 				aggregatePoly = append(aggregatePoly, evalPoly)

// 				c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
// 					commit.Commitment,
// 				)
// 				if err != nil {
// 					k.logger.Error("could not verify clock frame", zap.Error(err))
// 					return errors.Wrap(err, "verify frame")
// 				}
// 				commitments = append(commitments, c.(curves.PairingPoint))
// 			default:
// 				k.logger.Debug("confirming inclusion in aggregate")
// 				poly, err := k.prover.BytesToPolynomial(commit.Data)
// 				if err != nil {
// 					k.logger.Error(
// 						"error converting key bundle to polynomial",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}

// 				for i := 0; i < 1024-len(poly); i++ {
// 					poly = append(
// 						poly,
// 						curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
// 					)
// 				}

// 				evalPoly, err := kzg.FFT(
// 					poly,
// 					*curves.BLS48581(
// 						curves.BLS48581G1().NewGeneratorPoint(),
// 					),
// 					1024,
// 					false,
// 				)
// 				if err != nil {
// 					k.logger.Error(
// 						"error performing fast fourier transform on key bundle",
// 						zap.Error(err),
// 					)
// 					return errors.Wrap(err, "verify frame")
// 				}
// 				k.logger.Debug(
// 					"created fft of polynomial",
// 					zap.Int("poly_size", len(evalPoly)),
// 				)

// 				aggregatePoly = append(aggregatePoly, evalPoly)

// 				c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
// 					commit.Commitment,
// 				)
// 				if err != nil {
// 					k.logger.Error("could not verify clock frame", zap.Error(err))
// 					return errors.Wrap(err, "verify frame")
// 				}
// 				commitments = append(commitments, c.(curves.PairingPoint))
// 			}
// 		}

// 		p, err := curves.BLS48581G1().Point.FromAffineCompressed(
// 			proof.Proof,
// 		)
// 		if err != nil {
// 			k.logger.Error("could not verify clock frame", zap.Error(err))
// 			return errors.Wrap(err, "verify frame")
// 		}

// 		result, err := k.prover.VerifyAggregateProof(
// 			aggregatePoly,
// 			commitments,
// 			aggregateCommitments[i],
// 			p.(curves.PairingPoint),
// 		)
// 		if err != nil {
// 			k.logger.Error(
// 				"could not verify clock frame",
// 				zap.Error(err),
// 			)
// 			return errors.Wrap(err, "verify frame")
// 		}

// 		if !result {
// 			k.logger.Error("could not verify clock frame", zap.Error(err))
// 			return errors.Wrap(
// 				errors.New("invalid proof"),
// 				"verify frame",
// 			)
// 		}
// 	}

// 	return nil
// }

func (k *KZGInclusionProver) CommitRaw(
	data []byte,
	polySize uint64,
) ([]byte, error) {
	return rbls48581.CommitRaw(data, polySize), nil
}

func (k *KZGInclusionProver) ProveRaw(
	data []byte,
	index int,
	polySize uint64,
) ([]byte, error) {
	return rbls48581.ProveRaw(data, uint64(index), polySize), nil
}

func (k *KZGInclusionProver) VerifyRaw(
	data []byte,
	commit []byte,
	index int,
	proof []byte,
	polySize uint64,
) (bool, error) {
	return rbls48581.VerifyRaw(data, commit, uint64(index), proof, polySize), nil
}

var _ InclusionProver = (*KZGInclusionProver)(nil)
