// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.29.0
// source: payments.sql

package sqlc

import (
	"context"
	"database/sql"
	"strings"
	"time"
)

const countPayments = `-- name: CountPayments :one
SELECT COUNT(*) FROM payments
`

func (q *Queries) CountPayments(ctx context.Context) (int64, error) {
	row := q.db.QueryRowContext(ctx, countPayments)
	var count int64
	err := row.Scan(&count)
	return count, err
}

const deleteFailedAttempts = `-- name: DeleteFailedAttempts :exec
DELETE FROM payment_htlc_attempts WHERE payment_id = $1 AND attempt_index IN (
    SELECT attempt_index FROM payment_htlc_attempt_resolutions WHERE resolution_type = 2
)
`

// Delete all failed HTLC attempts for the given payment. Resolution type 2
// indicates a failed attempt.
func (q *Queries) DeleteFailedAttempts(ctx context.Context, paymentID int64) error {
	_, err := q.db.ExecContext(ctx, deleteFailedAttempts, paymentID)
	return err
}

const deletePayment = `-- name: DeletePayment :exec
DELETE FROM payments WHERE id = $1
`

func (q *Queries) DeletePayment(ctx context.Context, id int64) error {
	_, err := q.db.ExecContext(ctx, deletePayment, id)
	return err
}

const failAttempt = `-- name: FailAttempt :exec
INSERT INTO payment_htlc_attempt_resolutions (
    attempt_index,
    resolution_time,
    resolution_type,
    failure_source_index,
    htlc_fail_reason,
    failure_msg
)
VALUES (
    $1,
    $2,
    $3,
    $4,
    $5,
    $6
)
`

type FailAttemptParams struct {
	AttemptIndex       int64
	ResolutionTime     time.Time
	ResolutionType     int32
	FailureSourceIndex sql.NullInt32
	HtlcFailReason     sql.NullInt32
	FailureMsg         []byte
}

func (q *Queries) FailAttempt(ctx context.Context, arg FailAttemptParams) error {
	_, err := q.db.ExecContext(ctx, failAttempt,
		arg.AttemptIndex,
		arg.ResolutionTime,
		arg.ResolutionType,
		arg.FailureSourceIndex,
		arg.HtlcFailReason,
		arg.FailureMsg,
	)
	return err
}

const failPayment = `-- name: FailPayment :execresult
UPDATE payments SET fail_reason = $1 WHERE payment_identifier = $2
`

type FailPaymentParams struct {
	FailReason        sql.NullInt32
	PaymentIdentifier []byte
}

func (q *Queries) FailPayment(ctx context.Context, arg FailPaymentParams) (sql.Result, error) {
	return q.db.ExecContext(ctx, failPayment, arg.FailReason, arg.PaymentIdentifier)
}

const fetchAllInflightAttempts = `-- name: FetchAllInflightAttempts :many
SELECT
    ha.id,
    ha.attempt_index,
    ha.payment_id,
    ha.session_key,
    ha.attempt_time,
    ha.payment_hash,
    ha.first_hop_amount_msat,
    ha.route_total_time_lock,
    ha.route_total_amount,
    ha.route_source_key
FROM payment_htlc_attempts ha
WHERE NOT EXISTS (
    SELECT 1 FROM payment_htlc_attempt_resolutions hr
    WHERE hr.attempt_index = ha.attempt_index
)
AND ha.attempt_index > $1
ORDER BY ha.attempt_index ASC
LIMIT $2
`

type FetchAllInflightAttemptsParams struct {
	AttemptIndex int64
	Limit        int32
}

// Fetch all inflight attempts with their payment data using pagination.
// Returns attempt data joined with payment and intent data to avoid separate queries.
func (q *Queries) FetchAllInflightAttempts(ctx context.Context, arg FetchAllInflightAttemptsParams) ([]PaymentHtlcAttempt, error) {
	rows, err := q.db.QueryContext(ctx, fetchAllInflightAttempts, arg.AttemptIndex, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []PaymentHtlcAttempt
	for rows.Next() {
		var i PaymentHtlcAttempt
		if err := rows.Scan(
			&i.ID,
			&i.AttemptIndex,
			&i.PaymentID,
			&i.SessionKey,
			&i.AttemptTime,
			&i.PaymentHash,
			&i.FirstHopAmountMsat,
			&i.RouteTotalTimeLock,
			&i.RouteTotalAmount,
			&i.RouteSourceKey,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchHopLevelCustomRecords = `-- name: FetchHopLevelCustomRecords :many
SELECT
    l.id,
    l.hop_id,
    l.key,
    l.value
FROM payment_hop_custom_records l
WHERE l.hop_id IN (/*SLICE:hop_ids*/?)
ORDER BY l.hop_id ASC, l.key ASC
`

func (q *Queries) FetchHopLevelCustomRecords(ctx context.Context, hopIds []int64) ([]PaymentHopCustomRecord, error) {
	query := fetchHopLevelCustomRecords
	var queryParams []interface{}
	if len(hopIds) > 0 {
		for _, v := range hopIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:hop_ids*/?", makeQueryParams(len(queryParams), len(hopIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:hop_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []PaymentHopCustomRecord
	for rows.Next() {
		var i PaymentHopCustomRecord
		if err := rows.Scan(
			&i.ID,
			&i.HopID,
			&i.Key,
			&i.Value,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchHopsForAttempts = `-- name: FetchHopsForAttempts :many
SELECT
    h.id,
    h.htlc_attempt_index,
    h.hop_index,
    h.pub_key,
    h.scid,
    h.outgoing_time_lock,
    h.amt_to_forward,
    h.meta_data,
    m.payment_addr AS mpp_payment_addr,
    m.total_msat AS mpp_total_msat,
    a.root_share AS amp_root_share,
    a.set_id AS amp_set_id,
    a.child_index AS amp_child_index,
    b.encrypted_data,
    b.blinding_point,
    b.blinded_path_total_amt
FROM payment_route_hops h
LEFT JOIN payment_route_hop_mpp m ON m.hop_id = h.id
LEFT JOIN payment_route_hop_amp a ON a.hop_id = h.id
LEFT JOIN payment_route_hop_blinded b ON b.hop_id = h.id
WHERE h.htlc_attempt_index IN (/*SLICE:htlc_attempt_indices*/?)
ORDER BY h.htlc_attempt_index ASC, h.hop_index ASC
`

type FetchHopsForAttemptsRow struct {
	ID                  int64
	HtlcAttemptIndex    int64
	HopIndex            int32
	PubKey              []byte
	Scid                string
	OutgoingTimeLock    int32
	AmtToForward        int64
	MetaData            []byte
	MppPaymentAddr      []byte
	MppTotalMsat        sql.NullInt64
	AmpRootShare        []byte
	AmpSetID            []byte
	AmpChildIndex       sql.NullInt32
	EncryptedData       []byte
	BlindingPoint       []byte
	BlindedPathTotalAmt sql.NullInt64
}

func (q *Queries) FetchHopsForAttempts(ctx context.Context, htlcAttemptIndices []int64) ([]FetchHopsForAttemptsRow, error) {
	query := fetchHopsForAttempts
	var queryParams []interface{}
	if len(htlcAttemptIndices) > 0 {
		for _, v := range htlcAttemptIndices {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:htlc_attempt_indices*/?", makeQueryParams(len(queryParams), len(htlcAttemptIndices)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:htlc_attempt_indices*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FetchHopsForAttemptsRow
	for rows.Next() {
		var i FetchHopsForAttemptsRow
		if err := rows.Scan(
			&i.ID,
			&i.HtlcAttemptIndex,
			&i.HopIndex,
			&i.PubKey,
			&i.Scid,
			&i.OutgoingTimeLock,
			&i.AmtToForward,
			&i.MetaData,
			&i.MppPaymentAddr,
			&i.MppTotalMsat,
			&i.AmpRootShare,
			&i.AmpSetID,
			&i.AmpChildIndex,
			&i.EncryptedData,
			&i.BlindingPoint,
			&i.BlindedPathTotalAmt,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchHtlcAttemptResolutionsForPayments = `-- name: FetchHtlcAttemptResolutionsForPayments :many
SELECT
    ha.payment_id,
    hr.resolution_type
FROM payment_htlc_attempts ha
LEFT JOIN payment_htlc_attempt_resolutions hr ON hr.attempt_index = ha.attempt_index
WHERE ha.payment_id IN (/*SLICE:payment_ids*/?)
`

type FetchHtlcAttemptResolutionsForPaymentsRow struct {
	PaymentID      int64
	ResolutionType sql.NullInt32
}

// Batch query to fetch only HTLC resolution status for multiple payments.
// We don't need to order by payment_id and attempt_time because we will
// group the resolutions by payment_id in the background.
func (q *Queries) FetchHtlcAttemptResolutionsForPayments(ctx context.Context, paymentIds []int64) ([]FetchHtlcAttemptResolutionsForPaymentsRow, error) {
	query := fetchHtlcAttemptResolutionsForPayments
	var queryParams []interface{}
	if len(paymentIds) > 0 {
		for _, v := range paymentIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", makeQueryParams(len(queryParams), len(paymentIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FetchHtlcAttemptResolutionsForPaymentsRow
	for rows.Next() {
		var i FetchHtlcAttemptResolutionsForPaymentsRow
		if err := rows.Scan(&i.PaymentID, &i.ResolutionType); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchHtlcAttemptsForPayments = `-- name: FetchHtlcAttemptsForPayments :many
SELECT
    ha.id,
    ha.attempt_index,
    ha.payment_id,
    ha.session_key,
    ha.attempt_time,
    ha.payment_hash,
    ha.first_hop_amount_msat,
    ha.route_total_time_lock,
    ha.route_total_amount,
    ha.route_source_key,
    hr.resolution_type,
    hr.resolution_time,
    hr.failure_source_index,
    hr.htlc_fail_reason,
    hr.failure_msg,
    hr.settle_preimage
FROM payment_htlc_attempts ha
LEFT JOIN payment_htlc_attempt_resolutions hr ON hr.attempt_index = ha.attempt_index
WHERE ha.payment_id IN (/*SLICE:payment_ids*/?)
ORDER BY ha.payment_id ASC, ha.attempt_time ASC
`

type FetchHtlcAttemptsForPaymentsRow struct {
	ID                 int64
	AttemptIndex       int64
	PaymentID          int64
	SessionKey         []byte
	AttemptTime        time.Time
	PaymentHash        []byte
	FirstHopAmountMsat int64
	RouteTotalTimeLock int32
	RouteTotalAmount   int64
	RouteSourceKey     []byte
	ResolutionType     sql.NullInt32
	ResolutionTime     sql.NullTime
	FailureSourceIndex sql.NullInt32
	HtlcFailReason     sql.NullInt32
	FailureMsg         []byte
	SettlePreimage     []byte
}

func (q *Queries) FetchHtlcAttemptsForPayments(ctx context.Context, paymentIds []int64) ([]FetchHtlcAttemptsForPaymentsRow, error) {
	query := fetchHtlcAttemptsForPayments
	var queryParams []interface{}
	if len(paymentIds) > 0 {
		for _, v := range paymentIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", makeQueryParams(len(queryParams), len(paymentIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FetchHtlcAttemptsForPaymentsRow
	for rows.Next() {
		var i FetchHtlcAttemptsForPaymentsRow
		if err := rows.Scan(
			&i.ID,
			&i.AttemptIndex,
			&i.PaymentID,
			&i.SessionKey,
			&i.AttemptTime,
			&i.PaymentHash,
			&i.FirstHopAmountMsat,
			&i.RouteTotalTimeLock,
			&i.RouteTotalAmount,
			&i.RouteSourceKey,
			&i.ResolutionType,
			&i.ResolutionTime,
			&i.FailureSourceIndex,
			&i.HtlcFailReason,
			&i.FailureMsg,
			&i.SettlePreimage,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchPayment = `-- name: FetchPayment :one
SELECT
    p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
    i.intent_type AS "intent_type",
    i.intent_payload AS "intent_payload"
FROM payments p
LEFT JOIN payment_intents i ON i.payment_id = p.id
WHERE p.payment_identifier = $1
`

type FetchPaymentRow struct {
	Payment       Payment
	IntentType    sql.NullInt16
	IntentPayload []byte
}

func (q *Queries) FetchPayment(ctx context.Context, paymentIdentifier []byte) (FetchPaymentRow, error) {
	row := q.db.QueryRowContext(ctx, fetchPayment, paymentIdentifier)
	var i FetchPaymentRow
	err := row.Scan(
		&i.Payment.ID,
		&i.Payment.AmountMsat,
		&i.Payment.CreatedAt,
		&i.Payment.PaymentIdentifier,
		&i.Payment.FailReason,
		&i.IntentType,
		&i.IntentPayload,
	)
	return i, err
}

const fetchPaymentDuplicates = `-- name: FetchPaymentDuplicates :many
SELECT
    id,
    payment_id,
    amount_msat,
    created_at,
    fail_reason,
    settle_preimage,
    settle_time
FROM payment_duplicates
WHERE payment_id = $1
ORDER BY id ASC
`

// Fetch all duplicate payment records from the payment_duplicates table for
// a given payment ID.
func (q *Queries) FetchPaymentDuplicates(ctx context.Context, paymentID int64) ([]PaymentDuplicate, error) {
	rows, err := q.db.QueryContext(ctx, fetchPaymentDuplicates, paymentID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []PaymentDuplicate
	for rows.Next() {
		var i PaymentDuplicate
		if err := rows.Scan(
			&i.ID,
			&i.PaymentID,
			&i.AmountMsat,
			&i.CreatedAt,
			&i.FailReason,
			&i.SettlePreimage,
			&i.SettleTime,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchPaymentLevelFirstHopCustomRecords = `-- name: FetchPaymentLevelFirstHopCustomRecords :many
SELECT
    l.id,
    l.payment_id,
    l.key,
    l.value
FROM payment_first_hop_custom_records l
WHERE l.payment_id IN (/*SLICE:payment_ids*/?)
ORDER BY l.payment_id ASC, l.key ASC
`

func (q *Queries) FetchPaymentLevelFirstHopCustomRecords(ctx context.Context, paymentIds []int64) ([]PaymentFirstHopCustomRecord, error) {
	query := fetchPaymentLevelFirstHopCustomRecords
	var queryParams []interface{}
	if len(paymentIds) > 0 {
		for _, v := range paymentIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", makeQueryParams(len(queryParams), len(paymentIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []PaymentFirstHopCustomRecord
	for rows.Next() {
		var i PaymentFirstHopCustomRecord
		if err := rows.Scan(
			&i.ID,
			&i.PaymentID,
			&i.Key,
			&i.Value,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchPaymentsByIDs = `-- name: FetchPaymentsByIDs :many
SELECT
    p.id,
    p.amount_msat,
    p.created_at,
    p.payment_identifier,
    p.fail_reason,
    pi.intent_type,
    pi.intent_payload
FROM payments p
LEFT JOIN payment_intents pi ON pi.payment_id = p.id
WHERE p.id IN (/*SLICE:payment_ids*/?)
ORDER BY p.id ASC
`

type FetchPaymentsByIDsRow struct {
	ID                int64
	AmountMsat        int64
	CreatedAt         time.Time
	PaymentIdentifier []byte
	FailReason        sql.NullInt32
	IntentType        sql.NullInt16
	IntentPayload     []byte
}

// Batch fetch payment and intent data for a set of payment IDs.
// Used to avoid fetching redundant payment data when processing multiple
// attempts for the same payment.
func (q *Queries) FetchPaymentsByIDs(ctx context.Context, paymentIds []int64) ([]FetchPaymentsByIDsRow, error) {
	query := fetchPaymentsByIDs
	var queryParams []interface{}
	if len(paymentIds) > 0 {
		for _, v := range paymentIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", makeQueryParams(len(queryParams), len(paymentIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FetchPaymentsByIDsRow
	for rows.Next() {
		var i FetchPaymentsByIDsRow
		if err := rows.Scan(
			&i.ID,
			&i.AmountMsat,
			&i.CreatedAt,
			&i.PaymentIdentifier,
			&i.FailReason,
			&i.IntentType,
			&i.IntentPayload,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchPaymentsByIDsMig = `-- name: FetchPaymentsByIDsMig :many
SELECT
    p.id,
    p.amount_msat,
    p.created_at,
    p.payment_identifier,
    p.fail_reason,
    COUNT(ha.id) AS htlc_attempt_count
FROM payments p
LEFT JOIN payment_htlc_attempts ha ON ha.payment_id = p.id
WHERE p.id IN (/*SLICE:payment_ids*/?)
GROUP BY p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason
ORDER BY p.id ASC
`

type FetchPaymentsByIDsMigRow struct {
	ID                int64
	AmountMsat        int64
	CreatedAt         time.Time
	PaymentIdentifier []byte
	FailReason        sql.NullInt32
	HtlcAttemptCount  int64
}

// Migration-specific batch fetch that returns payment data along with HTLC
// attempt counts for structural validation during KV to SQL migration.
func (q *Queries) FetchPaymentsByIDsMig(ctx context.Context, paymentIds []int64) ([]FetchPaymentsByIDsMigRow, error) {
	query := fetchPaymentsByIDsMig
	var queryParams []interface{}
	if len(paymentIds) > 0 {
		for _, v := range paymentIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", makeQueryParams(len(queryParams), len(paymentIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:payment_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FetchPaymentsByIDsMigRow
	for rows.Next() {
		var i FetchPaymentsByIDsMigRow
		if err := rows.Scan(
			&i.ID,
			&i.AmountMsat,
			&i.CreatedAt,
			&i.PaymentIdentifier,
			&i.FailReason,
			&i.HtlcAttemptCount,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const fetchRouteLevelFirstHopCustomRecords = `-- name: FetchRouteLevelFirstHopCustomRecords :many
SELECT
    l.id,
    l.htlc_attempt_index,
    l.key,
    l.value
FROM payment_attempt_first_hop_custom_records l
WHERE l.htlc_attempt_index IN (/*SLICE:htlc_attempt_indices*/?)
ORDER BY l.htlc_attempt_index ASC, l.key ASC
`

func (q *Queries) FetchRouteLevelFirstHopCustomRecords(ctx context.Context, htlcAttemptIndices []int64) ([]PaymentAttemptFirstHopCustomRecord, error) {
	query := fetchRouteLevelFirstHopCustomRecords
	var queryParams []interface{}
	if len(htlcAttemptIndices) > 0 {
		for _, v := range htlcAttemptIndices {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:htlc_attempt_indices*/?", makeQueryParams(len(queryParams), len(htlcAttemptIndices)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:htlc_attempt_indices*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []PaymentAttemptFirstHopCustomRecord
	for rows.Next() {
		var i PaymentAttemptFirstHopCustomRecord
		if err := rows.Scan(
			&i.ID,
			&i.HtlcAttemptIndex,
			&i.Key,
			&i.Value,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const filterPayments = `-- name: FilterPayments :many
/* ─────────────────────────────────────────────
   fetch queries
   ─────────────────────────────────────────────
*/

SELECT
    p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
    i.intent_type AS "intent_type",
    i.intent_payload AS "intent_payload"
FROM payments p
LEFT JOIN payment_intents i ON i.payment_id = p.id
WHERE p.id > COALESCE($1, -1)
  AND p.id < COALESCE($2, 9223372036854775807)
  -- NOTE: We use non-nullable time params with Go-side defaults instead of
  -- COALESCE, because COALESCE with text fallback causes type mismatch on
  -- Postgres (timestamp vs text), and OR-based optional filters can prevent
  -- the planner from using the created_at index.
  AND p.created_at >= $3
  AND p.created_at <= $4
  AND (
      i.intent_type = $5 OR
      $5 IS NULL OR i.intent_type IS NULL
  )
ORDER BY p.id ASC
LIMIT $6
`

type FilterPaymentsParams struct {
	IndexOffsetGet sql.NullInt64
	IndexOffsetLet sql.NullInt64
	CreatedAfter   time.Time
	CreatedBefore  time.Time
	IntentType     sql.NullInt16
	NumLimit       int32
}

type FilterPaymentsRow struct {
	Payment       Payment
	IntentType    sql.NullInt16
	IntentPayload []byte
}

func (q *Queries) FilterPayments(ctx context.Context, arg FilterPaymentsParams) ([]FilterPaymentsRow, error) {
	rows, err := q.db.QueryContext(ctx, filterPayments,
		arg.IndexOffsetGet,
		arg.IndexOffsetLet,
		arg.CreatedAfter,
		arg.CreatedBefore,
		arg.IntentType,
		arg.NumLimit,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FilterPaymentsRow
	for rows.Next() {
		var i FilterPaymentsRow
		if err := rows.Scan(
			&i.Payment.ID,
			&i.Payment.AmountMsat,
			&i.Payment.CreatedAt,
			&i.Payment.PaymentIdentifier,
			&i.Payment.FailReason,
			&i.IntentType,
			&i.IntentPayload,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const filterPaymentsDesc = `-- name: FilterPaymentsDesc :many
SELECT
    p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
    i.intent_type AS "intent_type",
    i.intent_payload AS "intent_payload"
FROM payments p
LEFT JOIN payment_intents i ON i.payment_id = p.id
WHERE p.id > COALESCE($1, -1)
  AND p.id < COALESCE($2, 9223372036854775807)
  -- NOTE: We use non-nullable time params with Go-side defaults instead of
  -- COALESCE, because COALESCE with text fallback causes type mismatch on
  -- Postgres (timestamp vs text), and OR-based optional filters can prevent
  -- the planner from using the created_at index.
  AND p.created_at >= $3
  AND p.created_at <= $4
  AND (
      i.intent_type = $5 OR
      $5 IS NULL OR i.intent_type IS NULL
  )
ORDER BY p.id DESC
LIMIT $6
`

type FilterPaymentsDescParams struct {
	IndexOffsetGet sql.NullInt64
	IndexOffsetLet sql.NullInt64
	CreatedAfter   time.Time
	CreatedBefore  time.Time
	IntentType     sql.NullInt16
	NumLimit       int32
}

type FilterPaymentsDescRow struct {
	Payment       Payment
	IntentType    sql.NullInt16
	IntentPayload []byte
}

func (q *Queries) FilterPaymentsDesc(ctx context.Context, arg FilterPaymentsDescParams) ([]FilterPaymentsDescRow, error) {
	rows, err := q.db.QueryContext(ctx, filterPaymentsDesc,
		arg.IndexOffsetGet,
		arg.IndexOffsetLet,
		arg.CreatedAfter,
		arg.CreatedBefore,
		arg.IntentType,
		arg.NumLimit,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []FilterPaymentsDescRow
	for rows.Next() {
		var i FilterPaymentsDescRow
		if err := rows.Scan(
			&i.Payment.ID,
			&i.Payment.AmountMsat,
			&i.Payment.CreatedAt,
			&i.Payment.PaymentIdentifier,
			&i.Payment.FailReason,
			&i.IntentType,
			&i.IntentPayload,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const insertHtlcAttempt = `-- name: InsertHtlcAttempt :one
INSERT INTO payment_htlc_attempts (
    payment_id,
    attempt_index,
    session_key,
    attempt_time,
    payment_hash,
    first_hop_amount_msat,
    route_total_time_lock,
    route_total_amount,
    route_source_key)
VALUES (
    $1,
    $2,
    $3,
    $4,
    $5, 
    $6, 
    $7, 
    $8, 
    $9)
RETURNING id
`

type InsertHtlcAttemptParams struct {
	PaymentID          int64
	AttemptIndex       int64
	SessionKey         []byte
	AttemptTime        time.Time
	PaymentHash        []byte
	FirstHopAmountMsat int64
	RouteTotalTimeLock int32
	RouteTotalAmount   int64
	RouteSourceKey     []byte
}

func (q *Queries) InsertHtlcAttempt(ctx context.Context, arg InsertHtlcAttemptParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertHtlcAttempt,
		arg.PaymentID,
		arg.AttemptIndex,
		arg.SessionKey,
		arg.AttemptTime,
		arg.PaymentHash,
		arg.FirstHopAmountMsat,
		arg.RouteTotalTimeLock,
		arg.RouteTotalAmount,
		arg.RouteSourceKey,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertPayment = `-- name: InsertPayment :one
INSERT INTO payments (
    amount_msat, 
    created_at, 
    payment_identifier,
    fail_reason)
VALUES (
    $1,
    $2,
    $3,
    NULL
)
RETURNING id
`

type InsertPaymentParams struct {
	AmountMsat        int64
	CreatedAt         time.Time
	PaymentIdentifier []byte
}

// Insert a new payment and return its ID.
// When creating a payment we don't have a fail reason because we start the
// payment process.
func (q *Queries) InsertPayment(ctx context.Context, arg InsertPaymentParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertPayment, arg.AmountMsat, arg.CreatedAt, arg.PaymentIdentifier)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertPaymentAttemptFirstHopCustomRecord = `-- name: InsertPaymentAttemptFirstHopCustomRecord :exec
INSERT INTO payment_attempt_first_hop_custom_records (
    htlc_attempt_index,
    key,
    value
)
VALUES (
    $1,
    $2,
    $3
)
`

type InsertPaymentAttemptFirstHopCustomRecordParams struct {
	HtlcAttemptIndex int64
	Key              int64
	Value            []byte
}

func (q *Queries) InsertPaymentAttemptFirstHopCustomRecord(ctx context.Context, arg InsertPaymentAttemptFirstHopCustomRecordParams) error {
	_, err := q.db.ExecContext(ctx, insertPaymentAttemptFirstHopCustomRecord, arg.HtlcAttemptIndex, arg.Key, arg.Value)
	return err
}

const insertPaymentDuplicateMig = `-- name: InsertPaymentDuplicateMig :one
INSERT INTO payment_duplicates (
    payment_id,
    amount_msat,
    created_at,
    fail_reason,
    settle_preimage,
    settle_time
)
VALUES (
    $1,
    $2,
    $3,
    $4,
    $5,
    $6
)
RETURNING id
`

type InsertPaymentDuplicateMigParams struct {
	PaymentID      int64
	AmountMsat     int64
	CreatedAt      time.Time
	FailReason     sql.NullInt32
	SettlePreimage []byte
	SettleTime     sql.NullTime
}

// Insert a duplicate payment record into the payment_duplicates table and
// return its ID.
func (q *Queries) InsertPaymentDuplicateMig(ctx context.Context, arg InsertPaymentDuplicateMigParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertPaymentDuplicateMig,
		arg.PaymentID,
		arg.AmountMsat,
		arg.CreatedAt,
		arg.FailReason,
		arg.SettlePreimage,
		arg.SettleTime,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertPaymentFirstHopCustomRecord = `-- name: InsertPaymentFirstHopCustomRecord :exec
INSERT INTO payment_first_hop_custom_records (
    payment_id,
    key,
    value
)
VALUES (
    $1,
    $2,
    $3
)
`

type InsertPaymentFirstHopCustomRecordParams struct {
	PaymentID int64
	Key       int64
	Value     []byte
}

func (q *Queries) InsertPaymentFirstHopCustomRecord(ctx context.Context, arg InsertPaymentFirstHopCustomRecordParams) error {
	_, err := q.db.ExecContext(ctx, insertPaymentFirstHopCustomRecord, arg.PaymentID, arg.Key, arg.Value)
	return err
}

const insertPaymentHopCustomRecord = `-- name: InsertPaymentHopCustomRecord :exec
INSERT INTO payment_hop_custom_records (
    hop_id,
    key,
    value
)
VALUES (
    $1,
    $2,
    $3
)
`

type InsertPaymentHopCustomRecordParams struct {
	HopID int64
	Key   int64
	Value []byte
}

func (q *Queries) InsertPaymentHopCustomRecord(ctx context.Context, arg InsertPaymentHopCustomRecordParams) error {
	_, err := q.db.ExecContext(ctx, insertPaymentHopCustomRecord, arg.HopID, arg.Key, arg.Value)
	return err
}

const insertPaymentIntent = `-- name: InsertPaymentIntent :one
INSERT INTO payment_intents (
    payment_id,
    intent_type, 
    intent_payload)
VALUES (
    $1,
    $2, 
    $3
)
RETURNING id
`

type InsertPaymentIntentParams struct {
	PaymentID     int64
	IntentType    int16
	IntentPayload []byte
}

// Insert a payment intent for a given payment and return its ID.
func (q *Queries) InsertPaymentIntent(ctx context.Context, arg InsertPaymentIntentParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertPaymentIntent, arg.PaymentID, arg.IntentType, arg.IntentPayload)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertPaymentMig = `-- name: InsertPaymentMig :one
/* ─────────────────────────────────────────────
   Migration-specific queries

   These queries are used ONLY for the one-time migration from KV to SQL.
   ─────────────────────────────────────────────
*/

INSERT INTO payments (
    amount_msat,
    created_at,
    payment_identifier,
    fail_reason)
VALUES (
    $1,
    $2,
    $3,
    $4
)
RETURNING id
`

type InsertPaymentMigParams struct {
	AmountMsat        int64
	CreatedAt         time.Time
	PaymentIdentifier []byte
	FailReason        sql.NullInt32
}

// Migration-specific payment insert that allows setting fail_reason.
// Normal InsertPayment forces fail_reason to NULL since new payments
// aren't failed yet. During migration, we're inserting historical data
// that may already be failed.
func (q *Queries) InsertPaymentMig(ctx context.Context, arg InsertPaymentMigParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertPaymentMig,
		arg.AmountMsat,
		arg.CreatedAt,
		arg.PaymentIdentifier,
		arg.FailReason,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertRouteHop = `-- name: InsertRouteHop :one
INSERT INTO payment_route_hops (
    htlc_attempt_index,
    hop_index,
    pub_key,
    scid,
    outgoing_time_lock,
    amt_to_forward,
    meta_data
)
VALUES (
    $1,
    $2,
    $3,
    $4,
    $5,
    $6,
    $7
)
RETURNING id
`

type InsertRouteHopParams struct {
	HtlcAttemptIndex int64
	HopIndex         int32
	PubKey           []byte
	Scid             string
	OutgoingTimeLock int32
	AmtToForward     int64
	MetaData         []byte
}

func (q *Queries) InsertRouteHop(ctx context.Context, arg InsertRouteHopParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertRouteHop,
		arg.HtlcAttemptIndex,
		arg.HopIndex,
		arg.PubKey,
		arg.Scid,
		arg.OutgoingTimeLock,
		arg.AmtToForward,
		arg.MetaData,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertRouteHopAmp = `-- name: InsertRouteHopAmp :exec
INSERT INTO payment_route_hop_amp (
    hop_id,
    root_share,
    set_id,
    child_index
)
VALUES (
    $1,
    $2,
    $3,
    $4
)
`

type InsertRouteHopAmpParams struct {
	HopID      int64
	RootShare  []byte
	SetID      []byte
	ChildIndex int32
}

func (q *Queries) InsertRouteHopAmp(ctx context.Context, arg InsertRouteHopAmpParams) error {
	_, err := q.db.ExecContext(ctx, insertRouteHopAmp,
		arg.HopID,
		arg.RootShare,
		arg.SetID,
		arg.ChildIndex,
	)
	return err
}

const insertRouteHopBlinded = `-- name: InsertRouteHopBlinded :exec
INSERT INTO payment_route_hop_blinded (
    hop_id,
    encrypted_data,
    blinding_point,
    blinded_path_total_amt
)
VALUES (
    $1,
    $2,
    $3,
    $4
)
`

type InsertRouteHopBlindedParams struct {
	HopID               int64
	EncryptedData       []byte
	BlindingPoint       []byte
	BlindedPathTotalAmt sql.NullInt64
}

func (q *Queries) InsertRouteHopBlinded(ctx context.Context, arg InsertRouteHopBlindedParams) error {
	_, err := q.db.ExecContext(ctx, insertRouteHopBlinded,
		arg.HopID,
		arg.EncryptedData,
		arg.BlindingPoint,
		arg.BlindedPathTotalAmt,
	)
	return err
}

const insertRouteHopMpp = `-- name: InsertRouteHopMpp :exec
INSERT INTO payment_route_hop_mpp (
    hop_id,
    payment_addr,
    total_msat
)
VALUES (
    $1,
    $2,
    $3
)
`

type InsertRouteHopMppParams struct {
	HopID       int64
	PaymentAddr []byte
	TotalMsat   int64
}

func (q *Queries) InsertRouteHopMpp(ctx context.Context, arg InsertRouteHopMppParams) error {
	_, err := q.db.ExecContext(ctx, insertRouteHopMpp, arg.HopID, arg.PaymentAddr, arg.TotalMsat)
	return err
}

const settleAttempt = `-- name: SettleAttempt :exec
INSERT INTO payment_htlc_attempt_resolutions (
    attempt_index,
    resolution_time,
    resolution_type,
    settle_preimage
)
VALUES (
    $1,
    $2,
    $3,
    $4
)
`

type SettleAttemptParams struct {
	AttemptIndex   int64
	ResolutionTime time.Time
	ResolutionType int32
	SettlePreimage []byte
}

func (q *Queries) SettleAttempt(ctx context.Context, arg SettleAttemptParams) error {
	_, err := q.db.ExecContext(ctx, settleAttempt,
		arg.AttemptIndex,
		arg.ResolutionTime,
		arg.ResolutionType,
		arg.SettlePreimage,
	)
	return err
}
