-- =====================================================================
-- Migration 0037 — Whish receive / payout support.
--
-- WHISH_SEND already exists. This adds WHISH_RECEIVE using the same
-- payout detail table and receive accounting as OMT/WU receive:
-- cash leaves the drawer, provider float increases because Whish owes
-- the shop settlement.
-- =====================================================================

set search_path = app, public;

insert into app.services(code, name, category) values
  ('WHISH_RECEIVE', 'Whish — Receive', 'money_transfer')
on conflict (code) do update
  set name = excluded.name,
      category = excluded.category;

-- Receive details can back OMT, WU, and Whish payout transactions.
create or replace function app.omt_recv_check()
returns trigger
language plpgsql
as $$
declare svc text;
begin
  select service_code into svc from app.transactions where id = new.txn_id;
  if svc not in ('OMT_RECEIVE','WU_RECEIVE','WHISH_RECEIVE') then
    raise exception 'omt_receive_details only valid for receive services (got %)', svc;
  end if;
  return new;
end;
$$;

-- Map Whish receive to the Whish float pool.
create or replace function app._money_transfer_provider(p_service text)
returns app.float_provider
language sql
immutable
as $$
  select case p_service
    when 'OMT_SEND'      then 'OMT_CASH'::app.float_provider
    when 'OMT_RECEIVE'   then 'OMT_CASH'::app.float_provider
    when 'OMT_BILL'      then 'OMT_CASH'::app.float_provider
    when 'WU_SEND'       then 'OMT_CASH'::app.float_provider
    when 'WU_RECEIVE'    then 'OMT_CASH'::app.float_provider
    when 'WHISH_SEND'    then 'WHISH'::app.float_provider
    when 'WHISH_RECEIVE' then 'WHISH'::app.float_provider
    when 'EDL_BILL'      then 'OMT_CASH'::app.float_provider
  end;
$$;

-- Re-define receive RPC to include WHISH_RECEIVE.
create or replace function app.record_omt_receive(
  p_shop uuid, p_till uuid,
  p_payment_method app.payment_method,
  p_gross_usd numeric, p_gross_lbp numeric,
  p_fee_usd numeric, p_fee_lbp numeric,
  p_commission_usd numeric, p_commission_lbp numeric,
  p_fx_rate numeric,
  p_payout_code text,
  p_beneficiary_full_name text,
  p_beneficiary_id_type app.id_doc_type,
  p_beneficiary_id_number text,
  p_beneficiary_phone text,
  p_origin_country text,
  p_kyc_doc_url text,
  p_customer_id uuid,
  p_notes text,
  p_service_code text default 'OMT_RECEIVE'
) returns uuid
language plpgsql
security definer
set search_path = app, public
as $$
declare
  v_txn          uuid;
  v_provider_lbl text;
  v_float_prov   app.float_provider;
  v_net_usd      numeric;
  v_net_lbp      numeric;
begin
  if p_service_code not in ('OMT_RECEIVE','WU_RECEIVE','WHISH_RECEIVE') then
    raise exception 'record_omt_receive: unsupported service %', p_service_code;
  end if;

  v_provider_lbl := case p_service_code
                      when 'OMT_RECEIVE'   then 'OMT'
                      when 'WU_RECEIVE'    then 'WU'
                      when 'WHISH_RECEIVE' then 'WHISH'
                    end;

  v_txn := app._insert_txn(p_shop, p_till, p_service_code, p_payment_method,
    p_gross_usd, p_gross_lbp, p_fee_usd, p_fee_lbp,
    p_commission_usd, p_commission_lbp, p_fx_rate,
    v_provider_lbl, p_payout_code,
    p_beneficiary_full_name, p_beneficiary_phone,
    p_customer_id, p_notes);

  insert into app.omt_receive_details(
    txn_id, payout_code,
    beneficiary_full_name, beneficiary_id_type, beneficiary_id_number,
    beneficiary_phone, origin_country, kyc_doc_url
  ) values (
    v_txn, p_payout_code,
    p_beneficiary_full_name, p_beneficiary_id_type, p_beneficiary_id_number,
    p_beneficiary_phone, p_origin_country, p_kyc_doc_url
  );

  v_net_usd := -coalesce(p_gross_usd,0) + coalesce(p_fee_usd,0);
  v_net_lbp := -coalesce(p_gross_lbp,0) + coalesce(p_fee_lbp,0);
  perform app._post_cash_for_txn(v_txn, p_payment_method, v_net_usd, v_net_lbp);

  v_float_prov := app._money_transfer_provider(p_service_code);
  perform app._post_float_for_txn(v_txn, v_float_prov, 'USD',
    coalesce(p_gross_usd,0) + coalesce(p_commission_usd,0),
    'receive: provider owes shop gross + commission');
  perform app._post_float_for_txn(v_txn, v_float_prov, 'LBP',
    coalesce(p_gross_lbp,0) + coalesce(p_commission_lbp,0),
    'receive: provider owes shop gross + commission');

  return v_txn;
end;
$$;

revoke all on function app.record_omt_receive(uuid, uuid, app.payment_method,
  numeric, numeric, numeric, numeric, numeric, numeric, numeric,
  text, text, app.id_doc_type, text, text, text, text, uuid, text, text) from public;
grant execute on function app.record_omt_receive(uuid, uuid, app.payment_method,
  numeric, numeric, numeric, numeric, numeric, numeric, numeric,
  text, text, app.id_doc_type, text, text, text, text, uuid, text, text) to authenticated;

-- Detail-required check with REFUND preservation from migration 0008.
create or replace function app.txn_require_detail()
returns trigger
language plpgsql
as $$
declare ok boolean;
begin
  if new.status <> 'completed' then return null; end if;
  case new.service_code
    when 'OMT_SEND'         then select exists(select 1 from app.omt_send_details      where txn_id = new.id) into ok;
    when 'OMT_RECEIVE'      then select exists(select 1 from app.omt_receive_details   where txn_id = new.id) into ok;
    when 'WU_SEND'          then select exists(select 1 from app.omt_send_details      where txn_id = new.id) into ok;
    when 'WU_RECEIVE'       then select exists(select 1 from app.omt_receive_details   where txn_id = new.id) into ok;
    when 'WHISH_SEND'       then select exists(select 1 from app.omt_send_details      where txn_id = new.id) into ok;
    when 'WHISH_RECEIVE'    then select exists(select 1 from app.omt_receive_details   where txn_id = new.id) into ok;
    when 'OMT_BILL'         then select exists(select 1 from app.bill_payment_details  where txn_id = new.id) into ok;
    when 'EDL_BILL'         then select exists(select 1 from app.bill_payment_details  where txn_id = new.id) into ok;
    when 'ALFA_RECHARGE'    then select exists(select 1 from app.recharge_details      where txn_id = new.id) into ok;
    when 'TOUCH_RECHARGE'   then select exists(select 1 from app.recharge_details      where txn_id = new.id) into ok;
    when 'OGERO_RECHARGE'   then select exists(select 1 from app.recharge_details      where txn_id = new.id) into ok;
    when 'INTERNET_RECHARGE'then select exists(select 1 from app.recharge_details      where txn_id = new.id) into ok;
    when 'SIM_SALE'         then select exists(select 1 from app.goods_sale_details    where txn_id = new.id) into ok;
    when 'PHONE_SALE'       then select exists(select 1 from app.goods_sale_details    where txn_id = new.id) into ok;
    when 'ACCESSORY_SALE'   then select exists(select 1 from app.goods_sale_details    where txn_id = new.id) into ok;
    when 'GOODS_SALE'       then select exists(select 1 from app.goods_sale_details    where txn_id = new.id) into ok;
    when 'REPAIR'           then select exists(select 1 from app.repair_details        where txn_id = new.id) into ok;
    when 'REFUND'           then select exists(select 1 from app.refunds               where refund_txn_id = new.id) into ok;
    else ok := true;
  end case;
  if not ok then
    raise exception 'transaction % (service %) is missing its detail/refund row',
      new.id, new.service_code;
  end if;
  return null;
end;
$$;

create or replace function app._money_transfer_require_movement()
returns trigger
language plpgsql
as $$
declare
  ok boolean;
  is_money_transfer boolean;
begin
  if new.status <> 'completed' then return null; end if;

  is_money_transfer := new.service_code in
    ('OMT_SEND','OMT_RECEIVE','OMT_BILL','EDL_BILL',
     'WU_SEND','WU_RECEIVE','WHISH_SEND','WHISH_RECEIVE');
  if not is_money_transfer then return null; end if;

  if coalesce(new.gross_usd,0) = 0 and coalesce(new.gross_lbp,0) = 0 then
    return null;
  end if;

  select exists (
    select 1 from app.float_movements
    where ref_txn_id = new.id
  ) into ok;
  if not ok then
    raise exception 'money-transfer txn % (service %) has no float_movement leg',
      new.id, new.service_code;
  end if;
  return null;
end;
$$;

drop index if exists app.ux_txn_external_ref_active;
create unique index ux_txn_external_ref_active
  on app.transactions (shop_id, external_ref_provider, external_ref)
  where external_ref is not null
    and external_ref_provider is not null
    and status <> 'voided'
    and service_code in (
      'OMT_SEND','OMT_RECEIVE','WU_SEND','WU_RECEIVE',
      'WHISH_SEND','WHISH_RECEIVE','OMT_BILL','EDL_BILL'
    );
