-- Add an atomic usage-record deletion path. The operation restores the
-- entitlement period and reflows stored balance snapshots for later rows.

CREATE FUNCTION condon.delete_usage_record(
  p_usage_record_id uuid
)
RETURNS void
LANGUAGE plpgsql
VOLATILE
SECURITY INVOKER
SET search_path = pg_catalog
AS $function$
DECLARE
  v_usage condon.usage_records%ROWTYPE;
  v_period condon.entitlement_periods%ROWTYPE;
  v_initial_balance integer;
  v_now timestamptz := pg_catalog.clock_timestamp();
BEGIN
  IF p_usage_record_id IS NULL THEN
    RAISE EXCEPTION USING
      ERRCODE = '22023',
      MESSAGE = 'CONDON_USAGE_RECORD_ID_REQUIRED';
  END IF;

  SELECT *
  INTO v_usage
  FROM condon.usage_records
  WHERE id = p_usage_record_id
  FOR UPDATE;

  IF NOT FOUND THEN
    RAISE EXCEPTION USING
      ERRCODE = 'P0002',
      MESSAGE = 'CONDON_USAGE_RECORD_NOT_FOUND';
  END IF;

  SELECT *
  INTO v_period
  FROM condon.entitlement_periods
  WHERE id = v_usage.entitlement_period_id
  FOR UPDATE;

  IF NOT FOUND THEN
    RAISE EXCEPTION USING
      ERRCODE = 'P0002',
      MESSAGE = 'CONDON_ENTITLEMENT_PERIOD_NOT_FOUND';
  END IF;

  DELETE FROM condon.entitlement_ledger
  WHERE usage_record_id = v_usage.id;

  DELETE FROM condon.usage_records
  WHERE id = v_usage.id;

  v_initial_balance := v_period.annual_grant + v_period.carry_forward;

  WITH ordered AS (
    SELECT
      ur.id,
      (
        v_initial_balance
        - COALESCE(
            SUM(ur.use_nights) OVER (
              ORDER BY
                CASE WHEN ur.source_sheet IS NULL THEN 1 ELSE 0 END,
                ur.source_sheet NULLS LAST,
                ur.source_sequence NULLS LAST,
                ur.source_row NULLS LAST,
                ur.created_at ASC,
                ur.id ASC
              ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
            ),
            0
          )
      )::integer AS balance_before,
      (
        v_initial_balance
        - SUM(ur.use_nights) OVER (
            ORDER BY
              CASE WHEN ur.source_sheet IS NULL THEN 1 ELSE 0 END,
              ur.source_sheet NULLS LAST,
              ur.source_sequence NULLS LAST,
              ur.source_row NULLS LAST,
              ur.created_at ASC,
              ur.id ASC
          )
      )::integer AS balance_after
    FROM condon.usage_records AS ur
    WHERE ur.entitlement_period_id = v_period.id
  )
  UPDATE condon.usage_records AS ur
  SET
    balance_before = ordered.balance_before,
    balance_after = ordered.balance_after
  FROM ordered
  WHERE ur.id = ordered.id;

  WITH ordered AS (
    SELECT
      ur.id,
      (
        v_initial_balance
        - COALESCE(
            SUM(ur.use_nights) OVER (
              ORDER BY
                CASE WHEN ur.source_sheet IS NULL THEN 1 ELSE 0 END,
                ur.source_sheet NULLS LAST,
                ur.source_sequence NULLS LAST,
                ur.source_row NULLS LAST,
                ur.created_at ASC,
                ur.id ASC
              ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
            ),
            0
          )
      )::integer AS balance_before,
      (
        v_initial_balance
        - SUM(ur.use_nights) OVER (
            ORDER BY
              CASE WHEN ur.source_sheet IS NULL THEN 1 ELSE 0 END,
              ur.source_sheet NULLS LAST,
              ur.source_sequence NULLS LAST,
              ur.source_row NULLS LAST,
              ur.created_at ASC,
              ur.id ASC
          )
      )::integer AS balance_after
    FROM condon.usage_records AS ur
    WHERE ur.entitlement_period_id = v_period.id
  )
  UPDATE condon.entitlement_ledger AS ledger
  SET
    balance_before = ordered.balance_before,
    balance_after = ordered.balance_after
  FROM ordered
  WHERE ledger.usage_record_id = ordered.id
    AND ledger.entry_type = 'usage';

  UPDATE condon.entitlement_periods
  SET
    current_balance = (
      v_initial_balance
      - COALESCE(
          (
            SELECT SUM(ur.use_nights)
            FROM condon.usage_records AS ur
            WHERE ur.entitlement_period_id = v_period.id
          ),
          0
        )
    )::integer,
    row_version = row_version + 1,
    updated_at = v_now
  WHERE id = v_period.id;

  DELETE FROM condon.bookings AS booking
  WHERE booking.confirmation_no = v_usage.confirmation_no
    AND NOT EXISTS (
      SELECT 1
      FROM condon.usage_records AS ur
      WHERE ur.confirmation_no = booking.confirmation_no
    );
END;
$function$;

REVOKE ALL ON FUNCTION condon.delete_usage_record(uuid) FROM PUBLIC;
