begin;

create table if not exists public.feature_flags (
  id uuid primary key default gen_random_uuid(),
  flag_key text not null check (flag_key ~ '^[a-z][a-z0-9_.-]{2,79}$'),
  version integer not null check (version > 0),
  enabled boolean not null default false,
  rollout_percentage integer not null default 100 check (rollout_percentage between 0 and 100),
  config jsonb not null default '{}'::jsonb check (jsonb_typeof(config)='object'),
  status text not null default 'draft' check (status in ('draft','published','retired')),
  created_by uuid references auth.users(id) on delete set null,
  created_at timestamptz not null default now(),
  published_at timestamptz,
  unique(flag_key,version)
);
create unique index if not exists feature_flags_one_draft_idx on public.feature_flags(flag_key) where status='draft';
create unique index if not exists feature_flags_one_published_idx on public.feature_flags(flag_key) where status='published';

create table if not exists public.notification_templates (
  id uuid primary key default gen_random_uuid(),
  template_key text not null check (template_key in ('subscription.expiring','payment.grant_failed','fair_use.blocked')),
  version integer not null check (version>0),
  channel text not null default 'in_app' check (channel in ('in_app','email')),
  subject text not null default '' check (char_length(subject)<=160),
  body text not null check (char_length(body) between 1 and 4000),
  status text not null default 'draft' check (status in ('draft','published','retired')),
  created_by uuid references auth.users(id) on delete set null,
  created_at timestamptz not null default now(),
  published_at timestamptz,
  unique(template_key,channel,version)
);
create unique index if not exists notification_templates_published_idx
  on public.notification_templates(template_key,channel) where status='published';

create table if not exists public.pricing_experiment_events (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete set null,
  experiment_key text not null,
  variant text not null,
  event_name text not null check (event_name in ('view','checkout','paid','cancelled')),
  product_code text,
  metadata jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default clock_timestamp()
);
create index if not exists pricing_experiment_events_created_idx on public.pricing_experiment_events(experiment_key,created_at desc);

insert into public.feature_flags(flag_key,version,enabled,rollout_percentage,config,status,created_at,published_at) values
  ('billing.subscriptions',1,true,100,'{}','published',now(),now()),
  ('models.database_catalog',1,true,100,'{}','published',now(),now()),
  ('models.circuit_breaker',1,false,0,'{"failureThreshold":5,"cooldownSeconds":300}','published',now(),now()),
  ('billing.pro_products',1,false,0,'{}','published',now(),now())
on conflict(flag_key,version) do nothing;

insert into public.notification_templates(template_key,version,channel,subject,body,status,published_at) values
  ('subscription.expiring',1,'in_app','会员即将到期','你的会员权益即将到期，可在账户页查看结束时间。','published',now()),
  ('payment.grant_failed',1,'in_app','支付权益待处理','支付已确认，但权益发放仍在重试。请勿重复付款。','published',now()),
  ('fair_use.blocked',1,'in_app','已达到合理使用上限','当前使用频率已达到会员合理使用上限，请稍后再试。','published',now())
on conflict(template_key,channel,version) do nothing;

create or replace function public.admin_save_feature_flag(
  p_actor_user_id uuid,p_flag_id uuid,p_flag_key text,p_enabled boolean,p_rollout_percentage integer,
  p_config jsonb,p_expected_version integer,p_reason text,p_request_id text
)
returns uuid language plpgsql security definer set search_path=''
as $$ declare v_id uuid; v_version integer;
begin
  if not public.admin_has_permission(p_actor_user_id,'ops.flags.write') then raise exception 'admin_permission_denied' using errcode='42501'; end if;
  if char_length(btrim(coalesce(p_reason,''))) not between 1 and 500 then raise exception 'admin_reason_required' using errcode='22023'; end if;
  if p_flag_id is null then
    select coalesce(max(version),0)+1 into v_version from public.feature_flags where flag_key=p_flag_key;
    insert into public.feature_flags(flag_key,version,enabled,rollout_percentage,config,created_by)
      values(p_flag_key,v_version,p_enabled,p_rollout_percentage,coalesce(p_config,'{}'::jsonb),p_actor_user_id) returning id into v_id;
  else
    update public.feature_flags set enabled=p_enabled,rollout_percentage=p_rollout_percentage,config=coalesce(p_config,'{}'::jsonb)
      where id=p_flag_id and status='draft' and version=p_expected_version returning id into v_id;
    if v_id is null then raise exception 'feature_flag_version_conflict' using errcode='40001'; end if;
  end if;
  insert into audit.admin_audit_logs(actor_user_id,actor_email,actor_role,action,target_type,target_id,after_value,
    request_id,permission_used,reason)
  select p_actor_user_id,lower(btrim(u.email)),'admin','ops.flag.draft.save','feature_flag',v_id,
    jsonb_build_object('flagKey',p_flag_key,'enabled',p_enabled,'rolloutPercentage',p_rollout_percentage),
    p_request_id,'ops.flags.write',btrim(p_reason) from identity.users u where u.id=p_actor_user_id on conflict do nothing;
  return v_id;
end $$;

create or replace function public.admin_publish_feature_flag(
  p_actor_user_id uuid,p_flag_id uuid,p_expected_version integer,p_reason text,p_request_id text
)
returns uuid language plpgsql security definer set search_path=''
as $$ declare v_flag public.feature_flags%rowtype;
begin
  if not public.admin_has_permission(p_actor_user_id,'ops.flags.write') then raise exception 'admin_permission_denied' using errcode='42501'; end if;
  if char_length(btrim(coalesce(p_reason,''))) not between 1 and 500 then raise exception 'admin_reason_required' using errcode='22023'; end if;
  select * into v_flag from public.feature_flags where id=p_flag_id and status='draft' and version=p_expected_version for update;
  if not found then raise exception 'feature_flag_version_conflict' using errcode='40001'; end if;
  update public.feature_flags set status='retired' where flag_key=v_flag.flag_key and status='published';
  update public.feature_flags set status='published',published_at=clock_timestamp() where id=p_flag_id;
  insert into audit.admin_audit_logs(actor_user_id,actor_email,actor_role,action,target_type,target_id,after_value,
    request_id,permission_used,reason)
  select p_actor_user_id,lower(btrim(u.email)),'admin','ops.flag.publish','feature_flag',p_flag_id,
    jsonb_build_object('flagKey',v_flag.flag_key,'version',v_flag.version,'enabled',v_flag.enabled),
    p_request_id,'ops.flags.write',btrim(p_reason) from identity.users u where u.id=p_actor_user_id on conflict do nothing;
  return p_flag_id;
end $$;

create or replace view public.admin_billing_overview as
select
  (select count(*) from public.user_subscriptions where status='active' and starts_at<=now() and ends_at>now())::bigint as active_subscriptions,
  (select count(*) from public.payment_orders where status='paid' and paid_at>=date_trunc('day',now()))::bigint as paid_orders_today,
  (select coalesce(sum(money_cents),0) from public.payment_orders where status='paid' and paid_at>=date_trunc('month',now()))::bigint as revenue_cents_month,
  (select count(*) from public.payment_orders where status='grant_pending' or grant_status='failed')::bigint as grant_failures,
  (select count(*) from public.usage_reservations where status='completed' and reserved_at>=date_trunc('day',now()))::bigint as completed_usage_today,
  (select coalesce(sum(cost_microusd),0) from public.usage_ledger where created_at>=date_trunc('month',now()))::bigint as model_cost_microusd_month;

alter table public.feature_flags enable row level security;
alter table public.notification_templates enable row level security;
alter table public.pricing_experiment_events enable row level security;
revoke all on table public.feature_flags,public.notification_templates,public.pricing_experiment_events from public,anon,authenticated;
grant select on table public.feature_flags,public.notification_templates,public.pricing_experiment_events to service_role;
revoke all on function public.admin_save_feature_flag(uuid,uuid,text,boolean,integer,jsonb,integer,text,text),
  public.admin_publish_feature_flag(uuid,uuid,integer,text,text) from public,anon,authenticated;
grant execute on function public.admin_save_feature_flag(uuid,uuid,text,boolean,integer,jsonb,integer,text,text),
  public.admin_publish_feature_flag(uuid,uuid,integer,text,text) to service_role;
do $$ begin if exists(select 1 from pg_roles where rolname='admin_runtime') then
  grant select on table public.feature_flags,public.notification_templates,public.pricing_experiment_events to admin_runtime;
  grant select on public.admin_billing_overview to admin_runtime;
  grant execute on function public.admin_save_feature_flag(uuid,uuid,text,boolean,integer,jsonb,integer,text,text),
    public.admin_publish_feature_flag(uuid,uuid,integer,text,text) to admin_runtime;
end if; end $$;

commit;
