package data

import (
	"time"
	"database/sql"
	"math/big"
	"context"

	"party.at/party/internal/crypto"

	"github.com/lib/pq"
)

type Vote struct {
	ID        int64     `json:"id"`
	OptionID  int64     `json:"option_id"`
	Nonce     []byte    `json:"nonce"`
	Signature []byte    `json:"signature"`
	Created   time.Time `json:"created"`
}

type VoteModel struct {
	DB *sql.DB
}


func (m VoteModel) CountForOption(optionID int64) (int64, error) {
	query := `
SELECT COUNT(v.id)
FROM options o
LEFT JOIN votes v ON v.option_id = o.id
WHERE o.id = $1
GROUP BY o.id`

	ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
	defer cancel()

	var count int64

	err := m.DB.QueryRowContext(ctx, query, optionID).Scan(&count)
	if err != nil {
		return 0, err
	}

	return count, err
}

func (m VoteModel) Insert(vote *Vote, issue *Issue) error {

	n := new(big.Int).SetBytes(issue.N)
	e := issue.E
 
	// Recompute m = SHA-256(issueID || optionID || nonce)
	expected := crypto.VoteMessage(issue.ID, vote.OptionID, vote.Nonce)
 
	// Verify: s^e mod n == m
	sig := new(big.Int).SetBytes(vote.Signature)
	eInt := big.NewInt(int64(e))
	recovered := new(big.Int).Exp(sig, eInt, n)
	expectedInt := new(big.Int).SetBytes(expected[:])
 
	if recovered.Cmp(expectedInt) != 0 {
		return ErrInvalidSignature
	}
 
	query :=
`INSERT INTO votes (option_id, nonce, signature)
VALUES ($1, $2, $3)
RETURNING id, created`

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	args := []interface{}{
		vote.OptionID,
		vote.Nonce,
		vote.Signature,
	}

	err := m.DB.QueryRowContext(ctx, query, args...).Scan(&vote.ID, &vote.Created)
	if pgErr, ok := err.(*pq.Error); ok {
		if pgErr.Code == "23505" {
			if pgErr.Constraint == "votes_signature_key" {
				return ErrDuplicateVote
			}
		}
	}
	if err != nil {
		return err
	}

	return nil
}
