-- ─────────────────────────────────────────────
-- Payment System Schema Migration
-- ─────────────────────────────────────────────
-- This migration creates the complete payment system schema including:
-- - Payment intents (only BOLT 11 invoices for now)
-- - Payment attempts and HTLC tracking
-- - Route hops and custom TLV records
-- - Resolution tracking for settled/failed payments
-- ─────────────────────────────────────────────

-- ─────────────────────────────────────────────
-- Payments Table
-- ─────────────────────────────────────────────
-- Stores all payments including all known payment types:
-- - Legacy payments
-- - Multi-Path Payments (MPP)
-- - Atomic Multi-Path Payments (AMP)
-- - Blinded payments
-- - Keysend payments
-- - Spontaneous AMP payments
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payments (
    -- Primary key for the payment record
    id INTEGER PRIMARY KEY,

    -- The amount of the payment in millisatoshis
    amount_msat BIGINT NOT NULL,

    -- Timestamp when the payment was created
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- Logical identifier for the payment
    -- For legacy + MPP: matches the HTLC hash
    -- For AMP: the setID
    -- For future intent types: any unique payment-level key
    payment_identifier BLOB NOT NULL,

    -- The reason for payment failure (only set if payment has failed)
    -- Integer enum type indicating failure reason
    fail_reason INTEGER,

    -- Ensure payment identifiers are unique across all payments
    CONSTRAINT idx_payments_payment_identifier_unique
    UNIQUE (payment_identifier)
);

-- Index for efficient querying by creation time (for chronological ordering)
CREATE INDEX IF NOT EXISTS idx_payments_created_at
ON payments(created_at);

-- ─────────────────────────────────────────────
-- Payment Intents Table
-- ─────────────────────────────────────────────
-- Stores the descriptor of what the payment is paying for.
-- Depending on the type, the payload might contain:
-- - BOLT 11 invoice data
-- - BOLT 12 offer data
-- - NULL for legacy hash-only/keysend style payments
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_intents (
    -- Primary key for the intent record
    id INTEGER PRIMARY KEY,

    -- Reference to the payment this intent belongs to (one-to-one relationship)
    -- When the payment is deleted, the intent is automatically deleted
    payment_id BIGINT NOT NULL REFERENCES payments (id) ON DELETE CASCADE,

    -- The type of intent (e.g. 0 = bolt11_invoice, 1 = bolt12_invoice)
    -- Uses SMALLINT (int16) for efficient storage of enum values
    intent_type SMALLINT NOT NULL,

    -- The serialized payload for the payment intent
    -- Content depends on type - could be invoice, offer, or NULL
    intent_payload BLOB,

    -- Ensure one-to-one relationship: each payment has at most one intent.
    -- Currently we only support one intent per payment this makes sure we do
    -- not accidentally pay the same request multiple times. This currently
    -- only has bolt 11 payment requests/invoices. But in the future this can
    -- also include BOLT 12 offers/invoices.
    CONSTRAINT idx_payment_intents_payment_id_unique
    UNIQUE (payment_id)
);

-- Index for efficient querying by intent type
CREATE INDEX IF NOT EXISTS idx_payment_intents_type
ON payment_intents(intent_type);

-- ─────────────────────────────────────────────
-- Payment HTLC Attempts Table
-- ─────────────────────────────────────────────
-- Stores all HTLC attempts for a payment. A payment can have multiple
-- HTLC attempts depending on whether the payment is split and also
-- if some attempts fail and need to be retried.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_htlc_attempts (
    -- Primary key for the HTLC attempt record
    id INTEGER PRIMARY KEY,

    -- The index of the HTLC attempt
    -- TODO: This will be removed and the primary key will be used only
    attempt_index BIGINT NOT NULL,
    
    -- Reference to the parent payment
    payment_id BIGINT NOT NULL REFERENCES payments (id) ON DELETE CASCADE,

    -- The session key of the HTLC attempt (also known as ephemeral key
    -- of the Sphinx packet used for onion routing)
    session_key BLOB NOT NULL,
    
    -- Timestamp when the HTLC attempt was created
    attempt_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- The payment hash for the payment attempt
    -- The hash the HTLC will be locked to - this does not need to be
    -- equal to the payment level identifier (e.g., for AMP payments)
    payment_hash BLOB NOT NULL,

    -- First hop amount in millisatoshis of the HTLC attempt
    -- Normally the same as the total amount of the route, but when using
    -- custom channels this might be different
    first_hop_amount_msat BIGINT NOT NULL,

    -- ─────────────────────────────────────────────
    -- Route Information for the HTLC Attempt
    -- ─────────────────────────────────────────────
    -- Every attempt has one route, so there is a 1:1 relationship between
    -- attempts and routes. The route itself can be found in the hops table.
    -- ─────────────────────────────────────────────

    -- The total time lock of the route (in blocks)
    route_total_time_lock INTEGER NOT NULL,

    -- The total amount of the route in millisatoshis
    route_total_amount BIGINT NOT NULL,

    -- The source key of the route (our node's public key)
    route_source_key BLOB NOT NULL,

    -- Ensure attempt indices are unique across all attempts
    CONSTRAINT idx_htlc_attempt_index_unique 
    UNIQUE (attempt_index),

    -- Ensure session keys are unique (each attempt has unique session key)
    CONSTRAINT idx_htlc_session_key_unique 
    UNIQUE (session_key)
);

-- Index for efficient querying by payment ID (find all attempts for a payment)
CREATE INDEX IF NOT EXISTS idx_htlc_payment_id 
ON payment_htlc_attempts(payment_id);

-- Index for efficient querying by attempt index (for lookups and joins)
CREATE INDEX IF NOT EXISTS idx_htlc_attempt_index 
ON payment_htlc_attempts(attempt_index);

-- Index for efficient querying by payment hash (for HTLC matching)
CREATE INDEX IF NOT EXISTS idx_htlc_payment_hash 
ON payment_htlc_attempts(payment_hash);

-- Index for efficient querying by attempt time (for chronological ordering)
CREATE INDEX IF NOT EXISTS idx_htlc_attempt_time 
ON payment_htlc_attempts(attempt_time);

-- ─────────────────────────────────────────────
-- HTLC Attempt Resolutions Table
-- ─────────────────────────────────────────────
-- Stores resolution metadata for HTLC attempts. Rows appear once an
-- attempt settles or fails, providing the final outcome and timing.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_htlc_attempt_resolutions (
    -- Primary key referencing the HTLC attempt
    -- TODO: This will be removed and the primary key will be used only
    attempt_index INTEGER PRIMARY KEY 
    REFERENCES payment_htlc_attempts (attempt_index) ON DELETE CASCADE,

    -- Timestamp when the attempt was resolved (settled or failed)
    resolution_time TIMESTAMP NOT NULL,

    -- Outcome of the attempt: 1 = settled, 2 = failed
    resolution_type INTEGER NOT NULL CHECK (resolution_type IN (1, 2)),

    -- Settlement payload (only populated for settled attempts)
    -- Contains the preimage that proves payment completion
    settle_preimage BLOB,

    -- Failure payload (only populated for failed attempts)
    -- Index of the node that sent the failure
    failure_source_index INTEGER,
    
    -- HTLC failure reason code
    htlc_fail_reason INTEGER,
    
    -- Failure message from the failing node, this message is binary encoded
    -- using the lightning wire protocol, see also lnwire/onion_error.go
    failure_msg BLOB,

    -- Ensure data integrity: settled attempts must have preimage,
    -- failed attempts must not have preimage
    CHECK (
        (resolution_type = 1 AND settle_preimage IS NOT NULL AND 
         failure_source_index IS NULL AND htlc_fail_reason IS NULL AND 
         failure_msg IS NULL)
        OR
        (resolution_type = 2 AND settle_preimage IS NULL)
    )
);

-- Index for efficient querying by resolution type (settled vs failed)
CREATE INDEX IF NOT EXISTS idx_htlc_resolutions_type 
ON payment_htlc_attempt_resolutions(resolution_type);

-- Index for efficient querying by resolution time (for chronological analysis)
CREATE INDEX IF NOT EXISTS idx_htlc_resolutions_time 
ON payment_htlc_attempt_resolutions(resolution_time);

-- ─────────────────────────────────────────────
-- Payment Route Hops Table
-- ─────────────────────────────────────────────
-- Stores the individual hops of a payment route. An attempt has only
-- one route, but a route can consist of several hops through the network.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_route_hops (
    -- Primary key for the hop record
    id INTEGER PRIMARY KEY,

    -- Reference to the HTLC attempt this hop belongs to
    htlc_attempt_index BIGINT NOT NULL 
    REFERENCES payment_htlc_attempts (attempt_index) ON DELETE CASCADE,

    -- The order/index of this hop within the route (0-based)
    hop_index INTEGER NOT NULL,

    -- The public key of the hop (node's public key)
    pub_key BLOB,

    -- The short channel ID of the hop (channel identifier)
    scid TEXT NOT NULL,

    -- The outgoing time lock of the hop (in blocks)
    outgoing_time_lock INTEGER NOT NULL,

    -- The amount to forward to the next hop (in millisatoshis)
    amt_to_forward BIGINT NOT NULL,

    -- The metadata blob transmitted to the hop (onion payload)
    meta_data BLOB,

    -- Ensure each attempt can only have one hop at each hop index
    -- This prevents duplicate hops in the same position
    CONSTRAINT idx_route_hops_unique_hop_per_attempt 
    UNIQUE (htlc_attempt_index, hop_index)
);

-- Index for efficient querying by attempt index (find all hops for an attempt)
CREATE INDEX IF NOT EXISTS idx_route_hops_htlc_attempt_index 
ON payment_route_hops(htlc_attempt_index);

-- ─────────────────────────────────────────────
-- Per-Hop Payload Tables
-- ─────────────────────────────────────────────
-- These tables store specialized payload data for different payment types.
-- Each table is only populated for hops that require that specific payload.
-- ─────────────────────────────────────────────

-- ─────────────────────────────────────────────
-- MPP (Multi-Path Payment) Payload Table
-- ─────────────────────────────────────────────
-- Stores MPP-specific payload data. Only present for the final hop
-- of an MPP attempt, containing payment address and total amount info.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_route_hop_mpp (
    -- Primary key referencing the hop
    hop_id INTEGER PRIMARY KEY 
    REFERENCES payment_route_hops (id) ON DELETE CASCADE,

    -- The payment address of the MPP path (for payment correlation)
    payment_addr BLOB NOT NULL,

    -- The total amount of the MPP payment in millisatoshis
    -- This is the sum of all parts in the multi-path payment
    total_msat BIGINT NOT NULL
);

-- ─────────────────────────────────────────────
-- AMP (Atomic Multi-Path Payment) Payload Table
-- ─────────────────────────────────────────────
-- Stores AMP-specific payload data. Only present for the final hop
-- of an AMP attempt, containing share information for atomicity.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_route_hop_amp (
    -- Primary key referencing the hop
    hop_id INTEGER PRIMARY KEY 
    REFERENCES payment_route_hops (id) ON DELETE CASCADE,

    -- The root share of the AMP path (for share reconstruction)
    root_share BLOB NOT NULL,

    -- The set ID of the AMP path (groups related AMP parts)
    set_id BLOB NOT NULL,

    -- The child index of the AMP path (identifies this part)
    child_index INTEGER NOT NULL
);

-- ─────────────────────────────────────────────
-- Blinded Route Payload Table
-- ─────────────────────────────────────────────
-- Stores blinded route payload data. Rows only exist for hops that
-- are part of a blinded path, providing privacy-preserving routing.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_route_hop_blinded (
    -- Primary key referencing the hop
    hop_id INTEGER PRIMARY KEY 
    REFERENCES payment_route_hops (id) ON DELETE CASCADE,

    -- The encrypted payload for the blinded hop
    encrypted_data BLOB NOT NULL,

    -- Only set for the introduction point of the blinded path
    -- Contains the blinding point for the introduction node
    blinding_point BLOB,

    -- Only set for the final hop in the blinded path
    -- Contains the total amount for the entire blinded path
    blinded_path_total_amt BIGINT
);

-- ─────────────────────────────────────────────
-- Custom TLV Records Tables
-- ─────────────────────────────────────────────
-- These tables store custom TLV (Type-Length-Value) records associated
-- with payments, attempts, and hops. This is a denormalized structure
-- designed to simplify cascade deletions, as each record is owned by
-- a single parent entity.
-- ─────────────────────────────────────────────

-- ─────────────────────────────────────────────
-- Payment-Level First Hop Custom Records
-- ─────────────────────────────────────────────
-- Stores custom TLV records that are part of the first hop of a payment.
-- These records are sent to the first hop and are payment-level data.
-- 
-- NOTE: This relates to the custom tlv record data which is sent to the first
-- hop in the wire message (UpdateAddHTLC) NOT the onion packet.
-- 
-- TODO(ziggie): We store mostly redundant data here and on the attempt level.
-- This might be improved in the future to reduce duplication.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_first_hop_custom_records (
    -- Primary key for the custom record
    id INTEGER PRIMARY KEY,
    
    -- Reference to the parent payment
    payment_id BIGINT NOT NULL REFERENCES payments (id) ON DELETE CASCADE,
    
    -- The TLV type identifier (must be >= 65536 for custom records)
    key BIGINT NOT NULL,
    
    -- The TLV value data
    value BLOB NOT NULL,
    
    -- Ensure we only store custom TLV records (not standard ones)
    CHECK (key >= 65536),
    
    -- Ensure each payment can only have one record per TLV type
    CONSTRAINT idx_payment_first_hop_custom_records_unique 
    UNIQUE (payment_id, key)
);

-- ─────────────────────────────────────────────
-- Attempt-Level First Hop Custom Records
-- ─────────────────────────────────────────────
-- Stores custom TLV records for the first hop on the route level.
-- These might be different from the payment-level first hop records
-- in case of custom channels or route-specific modifications.
-- 
-- NOTE: This relates to the custom tlv record data which is sent to the first
-- hop in the wire message (UpdateAddHTLC) NOT the onion packet.
-- 
-- TODO(ziggie): We store mostly redundant data here and on the payment level.
-- This might be improved in the future to reduce duplication.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_attempt_first_hop_custom_records (
    -- Primary key for the custom record
    id INTEGER PRIMARY KEY,
    
    -- Reference to the parent HTLC attempt
    htlc_attempt_index BIGINT NOT NULL 
    REFERENCES payment_htlc_attempts (attempt_index) ON DELETE CASCADE,
    
    -- The TLV type identifier (must be >= 65536 for custom records)
    key BIGINT NOT NULL,
    
    -- The TLV value data
    value BLOB NOT NULL,
    
    -- Ensure we only store custom TLV records (not standard ones)
    CHECK (key >= 65536),
    
    -- Ensure each attempt can only have one record per TLV type
    CONSTRAINT idx_payment_attempt_first_hop_custom_records_unique 
    UNIQUE (htlc_attempt_index, key)
);

-- ─────────────────────────────────────────────
-- Hop-Level Custom Records
-- ─────────────────────────────────────────────
-- Stores custom TLV records associated with a specific hop within
-- a payment route. These records are sent to that specific hop
-- and are hop-specific data.
-- ─────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS payment_hop_custom_records (
    -- Primary key for the custom record
    id INTEGER PRIMARY KEY,
    
    -- Reference to the parent hop
    hop_id BIGINT NOT NULL REFERENCES payment_route_hops (id) ON DELETE CASCADE,
    
    -- The TLV type identifier (must be >= 65536 for custom records)
    key BIGINT NOT NULL,
    
    -- The TLV value data
    value BLOB NOT NULL,
    
    -- Ensure we only store custom TLV records (not standard ones)
    CHECK (key >= 65536),
    
    -- Ensure each hop can only have one record per TLV type
    CONSTRAINT idx_payment_hop_custom_records_unique 
    UNIQUE (hop_id, key)
);