-- Plesk Agency Portal MVP schema
-- Run this in Supabase SQL editor.

create extension if not exists pgcrypto;

create table if not exists agencies (
  id uuid primary key default gen_random_uuid(),
  name text not null,
  created_at timestamptz not null default now()
);

create table if not exists agency_members (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  user_id uuid not null,
  role text not null check (role in ('owner', 'admin', 'member')),
  created_at timestamptz not null default now(),
  unique (agency_id, user_id)
);

create table if not exists clients (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  name text not null,
  email text,
  phone text,
  notes text,
  created_at timestamptz not null default now()
);

create table if not exists plesk_instances (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  name text not null,
  base_url text not null,
  auth_type text not null check (auth_type in ('api_key', 'basic', 'oauth_token')),
  auth_secret_encrypted text not null,
  status text not null default 'active' check (status in ('active', 'disabled')),
  last_connected_at timestamptz,
  created_at timestamptz not null default now()
);

create table if not exists plesk_subscriptions (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  plesk_instance_id uuid not null references plesk_instances(id) on delete cascade,
  client_id uuid references clients(id) on delete set null,

  plesk_subscription_id text not null,
  name text,
  status text,
  owner_login text,
  plan_name text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),

  unique (plesk_instance_id, plesk_subscription_id)
);

create table if not exists plesk_domains (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  plesk_instance_id uuid not null references plesk_instances(id) on delete cascade,
  subscription_id uuid not null references plesk_subscriptions(id) on delete cascade,

  plesk_domain_id text not null,
  domain_name text not null,
  status text,
  created_at timestamptz not null default now(),

  unique (plesk_instance_id, plesk_domain_id),
  unique (plesk_instance_id, domain_name)
);

create table if not exists actions_log (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  actor_user_id uuid,
  target_type text not null,
  target_id uuid,
  action text not null,
  status text not null default 'pending' check (status in ('pending', 'success', 'failed')),
  error_message text,
  metadata jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now()
);

create table if not exists billing_accounts (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,

  stripe_customer_id text,
  stripe_subscription_id text,
  plan_id text,
  subscription_status text,
  current_period_end timestamptz,

  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),

  unique (agency_id),
  unique (stripe_customer_id)
);

create table if not exists entitlements (
  id uuid primary key default gen_random_uuid(),
  agency_id uuid not null references agencies(id) on delete cascade,
  key text not null,
  value jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now(),
  unique (agency_id, key)
);

create or replace function set_updated_at()
returns trigger
language plpgsql
as $$
begin
  new.updated_at = now();
  return new;
end;
$$;

drop trigger if exists trg_plesk_subscriptions_updated_at on plesk_subscriptions;
create trigger trg_plesk_subscriptions_updated_at
before update on plesk_subscriptions
for each row execute function set_updated_at();

drop trigger if exists trg_billing_accounts_updated_at on billing_accounts;
create trigger trg_billing_accounts_updated_at
before update on billing_accounts
for each row execute function set_updated_at();

create or replace function is_agency_member(target_agency_id uuid)
returns boolean
language sql
stable
security definer
set search_path = public
as $$
  select exists (
    select 1
    from agency_members am
    where am.agency_id = target_agency_id
      and am.user_id = auth.uid()
  );
$$;

create or replace function is_agency_admin(target_agency_id uuid)
returns boolean
language sql
stable
security definer
set search_path = public
as $$
  select exists (
    select 1
    from agency_members am
    where am.agency_id = target_agency_id
      and am.user_id = auth.uid()
      and am.role in ('owner', 'admin')
  );
$$;

alter table agencies enable row level security;
alter table agency_members enable row level security;
alter table clients enable row level security;
alter table plesk_instances enable row level security;
alter table plesk_subscriptions enable row level security;
alter table plesk_domains enable row level security;
alter table actions_log enable row level security;
alter table billing_accounts enable row level security;
alter table entitlements enable row level security;

drop policy if exists agencies_select on agencies;
create policy agencies_select on agencies
for select using (is_agency_member(id));

drop policy if exists agencies_update on agencies;
create policy agencies_update on agencies
for update using (is_agency_admin(id));

drop policy if exists agency_members_select on agency_members;
create policy agency_members_select on agency_members
for select using (is_agency_member(agency_id));

drop policy if exists agency_members_manage on agency_members;
create policy agency_members_manage on agency_members
for all using (is_agency_admin(agency_id))
with check (is_agency_admin(agency_id));

drop policy if exists clients_member_access on clients;
create policy clients_member_access on clients
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists plesk_instances_member_access on plesk_instances;
create policy plesk_instances_member_access on plesk_instances
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists plesk_subscriptions_member_access on plesk_subscriptions;
create policy plesk_subscriptions_member_access on plesk_subscriptions
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists plesk_domains_member_access on plesk_domains;
create policy plesk_domains_member_access on plesk_domains
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists actions_log_member_access on actions_log;
create policy actions_log_member_access on actions_log
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists billing_accounts_member_access on billing_accounts;
create policy billing_accounts_member_access on billing_accounts
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));

drop policy if exists entitlements_member_access on entitlements;
create policy entitlements_member_access on entitlements
for all using (is_agency_member(agency_id))
with check (is_agency_member(agency_id));
