-- PolyWeather minimal commerce/auth schema (P0)
-- Run in Supabase SQL editor.

create extension if not exists pgcrypto;

create table if not exists public.profiles (
  id uuid primary key references auth.users(id) on delete cascade,
  email text not null default '',
  telegram_user_id bigint,
  telegram_username text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists idx_profiles_email
  on public.profiles(email)
  include (id);

create index if not exists idx_profiles_id_lookup
  on public.profiles(id)
  include (email, created_at);

create table if not exists public.subscriptions (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  plan_code text not null,
  status text not null check (status in ('active', 'paused', 'expired', 'cancelled')),
  starts_at timestamptz not null default now(),
  expires_at timestamptz not null,
  source text not null default 'manual',
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists idx_subscriptions_user_status_expiry
  on public.subscriptions(user_id, expires_at desc)
  include (id, starts_at, plan_code, source)
  where status = 'active';

create index if not exists idx_subscriptions_status_expiry
  on public.subscriptions(expires_at asc)
  include (user_id, starts_at, plan_code)
  where status = 'active';

create index if not exists idx_subscriptions_user_created
  on public.subscriptions(user_id, created_at desc)
  include (id, status, plan_code, source, starts_at, expires_at, updated_at);

create table if not exists public.payments (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  amount numeric(18, 6) not null,
  currency text not null default 'USDC',
  chain text not null default 'polygon',
  tx_hash text unique,
  status text not null check (status in ('pending', 'confirmed', 'failed', 'refunded')),
  raw_payload jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists idx_payments_created_at
  on public.payments(created_at desc)
  include (id, user_id, amount, currency, chain, tx_hash, status);

create table if not exists public.entitlement_events (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  action text not null,
  reason text not null default '',
  actor text not null default 'system',
  payload jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now()
);

create table if not exists public.user_wallets (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  chain_id integer not null default 137,
  address text not null,
  status text not null default 'active' check (status in ('active', 'revoked')),
  is_primary boolean not null default false,
  verified_at timestamptz,
  last_used_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  unique(chain_id, address)
);

create index if not exists idx_user_wallets_user_chain
  on public.user_wallets(user_id, chain_id, is_primary desc, verified_at desc)
  include (id, address)
  where status = 'active';

create index if not exists idx_user_wallets_chain_address_owner
  on public.user_wallets(chain_id, address)
  include (user_id, status);

create table if not exists public.trial_claims (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  email text not null default '',
  telegram_user_id bigint,
  primary_wallet_address text,
  metadata jsonb not null default '{}'::jsonb,
  claimed_at timestamptz not null default now(),
  created_at timestamptz not null default now()
);

create unique index if not exists uq_trial_claims_user
  on public.trial_claims(user_id);

create unique index if not exists uq_trial_claims_email
  on public.trial_claims(lower(email))
  where email <> '';

create unique index if not exists uq_trial_claims_telegram
  on public.trial_claims(telegram_user_id)
  where telegram_user_id is not null;

create table if not exists public.trial_claim_wallets (
  id bigserial primary key,
  trial_claim_id bigint not null references public.trial_claims(id) on delete cascade,
  wallet_address text not null,
  created_at timestamptz not null default now()
);

create unique index if not exists uq_trial_claim_wallets_address
  on public.trial_claim_wallets(lower(wallet_address));

create table if not exists public.referral_codes (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  code text not null,
  status text not null default 'active' check (status in ('active', 'revoked')),
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create unique index if not exists uq_referral_codes_user
  on public.referral_codes(user_id);

create unique index if not exists uq_referral_codes_code
  on public.referral_codes(upper(code));

create table if not exists public.referral_attributions (
  id bigserial primary key,
  referrer_user_id uuid not null references auth.users(id) on delete cascade,
  referred_user_id uuid not null references auth.users(id) on delete cascade,
  code text not null,
  status text not null default 'pending' check (status in ('pending', 'converted', 'capped', 'cancelled')),
  converted_payment_intent_id text,
  converted_tx_hash text,
  converted_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  check (referrer_user_id <> referred_user_id)
);

create unique index if not exists uq_referral_attributions_referred
  on public.referral_attributions(referred_user_id);

create index if not exists idx_referral_attributions_pending
  on public.referral_attributions(referred_user_id, created_at desc)
  include (id, code, referrer_user_id)
  where status = 'pending';

create table if not exists public.referral_rewards (
  id bigserial primary key,
  referral_attribution_id bigint not null references public.referral_attributions(id) on delete cascade,
  referrer_user_id uuid not null references auth.users(id) on delete cascade,
  referred_user_id uuid not null references auth.users(id) on delete cascade,
  payment_intent_id text not null,
  tx_hash text,
  reward_days integer not null default 3 check (reward_days > 0 and reward_days <= 30),
  created_at timestamptz not null default now()
);

create unique index if not exists uq_referral_rewards_attribution
  on public.referral_rewards(referral_attribution_id);

create index if not exists idx_referral_rewards_referrer_month
  on public.referral_rewards(referrer_user_id, created_at desc)
  include (id, reward_days);

create table if not exists public.wallet_link_challenges (
  id bigserial primary key,
  user_id uuid not null references auth.users(id) on delete cascade,
  chain_id integer not null default 137,
  address text not null,
  nonce text not null unique,
  message text not null,
  expires_at timestamptz not null,
  consumed_at timestamptz,
  created_at timestamptz not null default now()
);

create table if not exists public.payment_intents (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users(id) on delete cascade,
  plan_code text not null,
  plan_id bigint not null,
  chain_id integer not null default 137,
  token_address text not null,
  receiver_address text not null,
  amount_units numeric(78,0) not null,
  payment_mode text not null default 'strict' check (payment_mode in ('strict', 'flex', 'direct')),
  allowed_wallet text,
  order_id_hex text not null unique,
  status text not null default 'created' check (status in ('created', 'submitted', 'confirmed', 'expired', 'failed', 'cancelled')),
  tx_hash text,
  expires_at timestamptz not null,
  confirmed_at timestamptz,
  metadata jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists idx_payment_intents_status_updated
  on public.payment_intents(status, updated_at desc)
  include (user_id)
  where status in ('submitted', 'confirmed');

create index if not exists idx_payment_intents_user_status_updated
  on public.payment_intents(user_id, status, updated_at desc);

create index if not exists idx_payment_intents_submitted_tx_updated
  on public.payment_intents(updated_at asc)
  include (id, user_id, tx_hash, chain_id)
  where status = 'submitted' and tx_hash is not null;

create index if not exists idx_payment_intents_user_created
  on public.payment_intents(user_id, created_at desc);

create index if not exists idx_payment_intents_tx_hash
  on public.payment_intents(tx_hash)
  include (id, user_id)
  where tx_hash is not null;

create table if not exists public.payment_transactions (
  id bigserial primary key,
  intent_id uuid not null references public.payment_intents(id) on delete cascade,
  tx_hash text not null unique,
  chain_id integer not null default 137,
  from_address text,
  to_address text not null,
  block_number bigint,
  payment_method text not null default 'wallet' check (payment_method in ('wallet', 'manual', 'direct')),
  status text not null default 'submitted' check (status in ('submitted', 'confirmed', 'failed', 'duplicate', 'refund_required')),
  raw_receipt jsonb not null default '{}'::jsonb,
  raw_tx jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists idx_payment_transactions_intent
  on public.payment_transactions(intent_id, created_at desc);

create index if not exists idx_payment_transactions_tx_hash_intent
  on public.payment_transactions(tx_hash)
  include (intent_id);

create or replace function public.sync_profile_from_auth()
returns trigger
language plpgsql
security definer
set search_path = public
as $$
begin
  insert into public.profiles (id, email)
  values (new.id, coalesce(new.email, ''))
  on conflict (id) do update
  set email = excluded.email,
      updated_at = now();
  return new;
end;
$$;

drop trigger if exists on_auth_user_created_polyweather on auth.users;
create trigger on_auth_user_created_polyweather
  after insert on auth.users
  for each row execute function public.sync_profile_from_auth();
