-- ============================================
-- TEMPLATE-SAAS: Commissions Functions
-- Version: 1.0.0
-- Module: SAAS-020
-- ============================================

-- ============================================
-- Function: Calculate tiered commission rate
-- Returns the applicable rate for a given amount based on tiers
-- ============================================
CREATE OR REPLACE FUNCTION commissions.apply_tiered_rate(
    p_tiers JSONB,
    p_amount DECIMAL(15, 2)
)
RETURNS DECIMAL(5, 2) AS $$
DECLARE
    v_tier JSONB;
    v_rate DECIMAL(5, 2) := 0;
BEGIN
    -- Tiers format: [{"from": 0, "to": 1000, "rate": 5}, {"from": 1000, "to": 5000, "rate": 7.5}, ...]
    -- Find the tier that matches the amount
    FOR v_tier IN SELECT * FROM jsonb_array_elements(p_tiers)
    LOOP
        IF p_amount >= (v_tier->>'from')::DECIMAL
           AND (v_tier->>'to' IS NULL OR p_amount < (v_tier->>'to')::DECIMAL) THEN
            v_rate := (v_tier->>'rate')::DECIMAL;
            EXIT;
        END IF;
    END LOOP;

    RETURN v_rate;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

COMMENT ON FUNCTION commissions.apply_tiered_rate IS 'Calculate commission rate from tiered configuration';

-- ============================================
-- Function: Calculate commission for a transaction
-- Returns the commission amount based on scheme configuration
-- ============================================
CREATE OR REPLACE FUNCTION commissions.calculate_commission(
    p_scheme_id UUID,
    p_user_id UUID,
    p_amount DECIMAL(15, 2),
    p_tenant_id UUID
)
RETURNS TABLE (
    rate_applied DECIMAL(5, 2),
    commission_amount DECIMAL(15, 2)
) AS $$
DECLARE
    v_scheme RECORD;
    v_assignment RECORD;
    v_rate DECIMAL(5, 2);
    v_commission DECIMAL(15, 2);
BEGIN
    -- Get scheme configuration
    SELECT * INTO v_scheme
    FROM commissions.schemes
    WHERE id = p_scheme_id
      AND tenant_id = p_tenant_id
      AND is_active = TRUE
      AND deleted_at IS NULL;

    IF NOT FOUND THEN
        RETURN QUERY SELECT 0::DECIMAL(5,2), 0::DECIMAL(15,2);
        RETURN;
    END IF;

    -- Check minimum amount threshold
    IF p_amount < v_scheme.min_amount THEN
        RETURN QUERY SELECT 0::DECIMAL(5,2), 0::DECIMAL(15,2);
        RETURN;
    END IF;

    -- Get user's custom rate if exists
    SELECT custom_rate INTO v_assignment
    FROM commissions.assignments
    WHERE scheme_id = p_scheme_id
      AND user_id = p_user_id
      AND tenant_id = p_tenant_id
      AND is_active = TRUE
      AND starts_at <= NOW()
      AND (ends_at IS NULL OR ends_at > NOW())
    LIMIT 1;

    -- Determine rate based on scheme type
    CASE v_scheme.type
        WHEN 'percentage' THEN
            v_rate := COALESCE(v_assignment.custom_rate, v_scheme.rate);
            v_commission := p_amount * (v_rate / 100);

        WHEN 'fixed' THEN
            v_rate := 0;
            v_commission := v_scheme.fixed_amount;

        WHEN 'tiered' THEN
            v_rate := commissions.apply_tiered_rate(v_scheme.tiers, p_amount);
            IF v_assignment.custom_rate IS NOT NULL THEN
                v_rate := v_assignment.custom_rate;
            END IF;
            v_commission := p_amount * (v_rate / 100);
    END CASE;

    -- Apply maximum cap if defined
    IF v_scheme.max_amount IS NOT NULL AND v_commission > v_scheme.max_amount THEN
        v_commission := v_scheme.max_amount;
    END IF;

    RETURN QUERY SELECT v_rate, ROUND(v_commission, 2);
END;
$$ LANGUAGE plpgsql STABLE;

COMMENT ON FUNCTION commissions.calculate_commission IS 'Calculate commission for a transaction based on scheme';

-- ============================================
-- Function: Close a commission period
-- Aggregates totals and updates status
-- ============================================
CREATE OR REPLACE FUNCTION commissions.close_period(
    p_period_id UUID,
    p_closed_by UUID
)
RETURNS BOOLEAN AS $$
DECLARE
    v_period RECORD;
    v_totals RECORD;
BEGIN
    -- Get period and verify it's open
    SELECT * INTO v_period
    FROM commissions.periods
    WHERE id = p_period_id
      AND status = 'open'
    FOR UPDATE;

    IF NOT FOUND THEN
        RETURN FALSE;
    END IF;

    -- Calculate totals from entries
    SELECT
        COUNT(*)::INT as entry_count,
        COALESCE(SUM(commission_amount), 0) as total
    INTO v_totals
    FROM commissions.entries
    WHERE period_id = p_period_id
      AND status IN ('pending', 'approved');

    -- Update period
    UPDATE commissions.periods
    SET status = 'closed',
        closed_at = NOW(),
        closed_by = p_closed_by,
        total_entries = v_totals.entry_count,
        total_amount = v_totals.total
    WHERE id = p_period_id;

    RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION commissions.close_period IS 'Close a commission period and calculate totals';

-- ============================================
-- Function: Get user earnings summary
-- Returns commission totals for a user
-- ============================================
CREATE OR REPLACE FUNCTION commissions.get_user_earnings(
    p_user_id UUID,
    p_tenant_id UUID,
    p_start_date TIMESTAMPTZ DEFAULT NULL,
    p_end_date TIMESTAMPTZ DEFAULT NULL
)
RETURNS TABLE (
    total_pending DECIMAL(15, 2),
    total_approved DECIMAL(15, 2),
    total_paid DECIMAL(15, 2),
    total_entries INT,
    currency VARCHAR(3)
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        COALESCE(SUM(CASE WHEN e.status = 'pending' THEN e.commission_amount ELSE 0 END), 0) as total_pending,
        COALESCE(SUM(CASE WHEN e.status = 'approved' THEN e.commission_amount ELSE 0 END), 0) as total_approved,
        COALESCE(SUM(CASE WHEN e.status = 'paid' THEN e.commission_amount ELSE 0 END), 0) as total_paid,
        COUNT(*)::INT as total_entries,
        COALESCE(MAX(e.currency), 'USD') as currency
    FROM commissions.entries e
    WHERE e.user_id = p_user_id
      AND e.tenant_id = p_tenant_id
      AND e.status NOT IN ('rejected', 'cancelled')
      AND (p_start_date IS NULL OR e.created_at >= p_start_date)
      AND (p_end_date IS NULL OR e.created_at <= p_end_date);
END;
$$ LANGUAGE plpgsql STABLE;

COMMENT ON FUNCTION commissions.get_user_earnings IS 'Get earnings summary for a user';

-- ============================================
-- Function: Auto-approve commissions after X days
-- Can be called by a scheduled job
-- ============================================
CREATE OR REPLACE FUNCTION commissions.auto_approve_pending(
    p_tenant_id UUID,
    p_days_threshold INT DEFAULT 7
)
RETURNS INT AS $$
DECLARE
    v_count INT;
BEGIN
    WITH updated AS (
        UPDATE commissions.entries
        SET status = 'approved',
            approved_at = NOW(),
            updated_at = NOW()
        WHERE tenant_id = p_tenant_id
          AND status = 'pending'
          AND created_at < NOW() - (p_days_threshold || ' days')::INTERVAL
        RETURNING id
    )
    SELECT COUNT(*) INTO v_count FROM updated;

    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION commissions.auto_approve_pending IS 'Auto-approve pending commissions after threshold days';
