-- =====================================================
-- P2P FIAT SYSTEM - RPC FUNCTIONS
-- Production-grade stored procedures
-- =====================================================

-- =====================================================
-- INCREMENT ESCROW BALANCE
-- =====================================================
CREATE OR REPLACE FUNCTION public.increment_escrow_balance(
  p_token TEXT,
  p_amount NUMERIC
) RETURNS void AS $$
BEGIN
  UPDATE public.platform_escrow_balance
  SET 
    total_locked = total_locked + p_amount,
    updated_at = NOW()
  WHERE token = p_token;
  
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Token % not found in escrow balance', p_token;
  END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- DECREMENT ESCROW BALANCE
-- =====================================================
CREATE OR REPLACE FUNCTION public.decrement_escrow_balance(
  p_token TEXT,
  p_amount NUMERIC
) RETURNS void AS $$
BEGIN
  UPDATE public.platform_escrow_balance
  SET 
    total_locked = total_locked - p_amount,
    updated_at = NOW()
  WHERE token = p_token;
  
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Token % not found in escrow balance', p_token;
  END IF;
  
  -- Check for negative balance (should never happen)
  IF (SELECT total_locked FROM public.platform_escrow_balance WHERE token = p_token) < 0 THEN
    RAISE EXCEPTION 'Escrow balance would go negative for token %', p_token;
  END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- UPDATE P2P REPUTATION AFTER TRADE
-- =====================================================
CREATE OR REPLACE FUNCTION public.update_p2p_reputation(
  p_seller_id UUID,
  p_buyer_id UUID,
  p_trade_id UUID
) RETURNS void AS $$
DECLARE
  v_trade RECORD;
  v_payment_time_minutes INT;
  v_confirmation_time_minutes INT;
BEGIN
  -- Get trade details
  SELECT * INTO v_trade
  FROM public.p2p_fiat_trades
  WHERE id = p_trade_id;
  
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Trade % not found', p_trade_id;
  END IF;
  
  -- Calculate timing metrics
  IF v_trade.buyer_marked_paid_at IS NOT NULL THEN
    v_payment_time_minutes := EXTRACT(EPOCH FROM (v_trade.buyer_marked_paid_at - v_trade.created_at)) / 60;
  END IF;
  
  IF v_trade.seller_confirmed_at IS NOT NULL AND v_trade.buyer_marked_paid_at IS NOT NULL THEN
    v_confirmation_time_minutes := EXTRACT(EPOCH FROM (v_trade.seller_confirmed_at - v_trade.buyer_marked_paid_at)) / 60;
  END IF;
  
  -- Update seller reputation
  INSERT INTO public.p2p_reputation (
    user_id,
    total_trades,
    completed_trades,
    total_as_seller,
    reputation_score,
    avg_confirmation_time_minutes,
    last_trade_at,
    first_trade_at
  ) VALUES (
    p_seller_id,
    1,
    1,
    1,
    105, -- +5 bonus for first trade
    v_confirmation_time_minutes,
    NOW(),
    NOW()
  )
  ON CONFLICT (user_id) DO UPDATE SET
    total_trades = p2p_reputation.total_trades + 1,
    completed_trades = p2p_reputation.completed_trades + 1,
    total_as_seller = p2p_reputation.total_as_seller + 1,
    reputation_score = LEAST(p2p_reputation.reputation_score + 5, 1000),
    avg_confirmation_time_minutes = CASE
      WHEN p2p_reputation.avg_confirmation_time_minutes IS NULL THEN v_confirmation_time_minutes
      ELSE (p2p_reputation.avg_confirmation_time_minutes + COALESCE(v_confirmation_time_minutes, 0)) / 2
    END,
    last_trade_at = NOW(),
    updated_at = NOW();
  
  -- Update buyer reputation
  INSERT INTO public.p2p_reputation (
    user_id,
    total_trades,
    completed_trades,
    total_as_buyer,
    reputation_score,
    avg_payment_time_minutes,
    last_trade_at,
    first_trade_at
  ) VALUES (
    p_buyer_id,
    1,
    1,
    1,
    105,
    v_payment_time_minutes,
    NOW(),
    NOW()
  )
  ON CONFLICT (user_id) DO UPDATE SET
    total_trades = p2p_reputation.total_trades + 1,
    completed_trades = p2p_reputation.completed_trades + 1,
    total_as_buyer = p2p_reputation.total_as_buyer + 1,
    reputation_score = LEAST(p2p_reputation.reputation_score + 5, 1000),
    avg_payment_time_minutes = CASE
      WHEN p2p_reputation.avg_payment_time_minutes IS NULL THEN v_payment_time_minutes
      ELSE (p2p_reputation.avg_payment_time_minutes + COALESCE(v_payment_time_minutes, 0)) / 2
    END,
    last_trade_at = NOW(),
    updated_at = NOW();
  
  -- Update trust levels based on reputation score
  UPDATE public.p2p_reputation
  SET trust_level = CASE
    WHEN reputation_score >= 900 THEN 'verified'
    WHEN reputation_score >= 700 THEN 'advanced'
    WHEN reputation_score >= 400 THEN 'intermediate'
    WHEN reputation_score >= 100 THEN 'basic'
    ELSE 'new'
  END,
  fast_trader = CASE
    WHEN avg_payment_time_minutes < 15 AND avg_confirmation_time_minutes < 30 THEN true
    ELSE false
  END
  WHERE user_id IN (p_seller_id, p_buyer_id);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- CANCEL EXPIRED TRADES (Cron job function)
-- =====================================================
CREATE OR REPLACE FUNCTION public.cancel_expired_trades()
RETURNS void AS $$
DECLARE
  v_trade RECORD;
BEGIN
  -- Cancel trades where buyer didn't pay in time
  FOR v_trade IN
    SELECT * FROM public.p2p_fiat_trades
    WHERE status = 'pending'
      AND payment_deadline < NOW()
  LOOP
    -- Update trade status
    UPDATE public.p2p_fiat_trades
    SET 
      status = 'cancelled',
      cancelled_by = seller_id,
      cancellation_reason = 'Payment deadline expired',
      updated_at = NOW()
    WHERE id = v_trade.id;
    
    -- Restore offer remaining amount
    UPDATE public.p2p_fiat_offers
    SET 
      remaining_amount = remaining_amount + v_trade.crypto_amount,
      status = CASE
        WHEN status = 'locked' THEN 'open'
        ELSE status
      END,
      updated_at = NOW()
    WHERE id = v_trade.offer_id;
    
    -- Update reputation (penalty for buyer)
    UPDATE public.p2p_reputation
    SET 
      cancelled_trades = cancelled_trades + 1,
      reputation_score = GREATEST(reputation_score - 10, 0),
      updated_at = NOW()
    WHERE user_id = v_trade.buyer_id;
  END LOOP;
  
  -- Auto-release trades where seller didn't confirm in time
  FOR v_trade IN
    SELECT * FROM public.p2p_fiat_trades
    WHERE status = 'payment_sent'
      AND confirmation_deadline < NOW()
  LOOP
    -- Mark as completed (auto-release)
    UPDATE public.p2p_fiat_trades
    SET 
      seller_confirmed_at = NOW(),
      status = 'completed',
      completed_at = NOW(),
      updated_at = NOW()
    WHERE id = v_trade.id;
    
    -- Note: Actual blockchain release must be done by backend service
    -- This just marks the trade as ready for release
    
    -- Update reputations
    PERFORM public.update_p2p_reputation(v_trade.seller_id, v_trade.buyer_id, v_trade.id);
  END LOOP;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- CANCEL EXPIRED OFFERS
-- =====================================================
CREATE OR REPLACE FUNCTION public.cancel_expired_offers()
RETURNS void AS $$
BEGIN
  UPDATE public.p2p_fiat_offers
  SET 
    status = 'cancelled',
    updated_at = NOW()
  WHERE status = 'open'
    AND expires_at < NOW();
  
  -- Note: Escrow refunds must be processed by backend service
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- GET PAYMENT METHOD DETAILS
-- =====================================================
CREATE OR REPLACE FUNCTION public.get_payment_method_details(
  p_offer_id UUID,
  p_requesting_user_id UUID
) RETURNS TABLE(
  method_name TEXT,
  payment_details JSONB
) AS $$
DECLARE
  v_offer RECORD;
  v_trade RECORD;
BEGIN
  -- Get offer
  SELECT * INTO v_offer
  FROM public.p2p_fiat_offers
  WHERE id = p_offer_id;
  
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Offer not found';
  END IF;
  
  -- Check if user is involved in an active trade for this offer
  SELECT * INTO v_trade
  FROM public.p2p_fiat_trades
  WHERE offer_id = p_offer_id
    AND buyer_id = p_requesting_user_id
    AND status IN ('pending', 'payment_sent')
  LIMIT 1;
  
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Unauthorized: You must have an active trade to view payment details';
  END IF;
  
  -- Return decrypted payment details
  RETURN QUERY
  SELECT 
    pm.method_name,
    CASE
      WHEN v_offer.payment_details_encrypted IS NOT NULL THEN
        pgsodium.crypto_aead_aes256gcm_decrypt(
          pgsodium.url_decode((v_offer.payment_details_encrypted->>'ciphertext')::TEXT),
          NULL, -- No associated data used in this context
          pgsodium.url_decode((v_offer.payment_details_encrypted->>'nonce')::TEXT),
          pg_catalog.get_secret_variable('app.app_encryption_key')::BYTEA
        )::TEXT::JSONB
      ELSE
        NULL
    END AS payment_details
  FROM public.payment_methods pm
  WHERE pm.id = v_offer.payment_method_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =====================================================
-- GRANT EXECUTE PERMISSIONS
-- =====================================================
GRANT EXECUTE ON FUNCTION public.increment_escrow_balance TO authenticated;
GRANT EXECUTE ON FUNCTION public.decrement_escrow_balance TO authenticated;
GRANT EXECUTE ON FUNCTION public.update_p2p_reputation TO authenticated;
GRANT EXECUTE ON FUNCTION public.cancel_expired_trades TO authenticated;
GRANT EXECUTE ON FUNCTION public.cancel_expired_offers TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_payment_method_details TO authenticated;
