create extension if not exists pgcrypto; create or replace function public.set_updated_at() returns trigger language plpgsql as $$ begin new.updated_at = now(); return new; end; $$; create table if not exists public.customers ( id uuid primary key default gen_random_uuid(), name text, "remoteJid" text not null unique, preferred_language text check (preferred_language in ('ar', 'en')), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table if not exists public.drivers ( id uuid primary key default gen_random_uuid(), customer_id uuid not null unique references public.customers(id) on delete cascade, status text not null default 'active' check (status in ('active', 'inactive', 'suspended')), rating numeric(3,2), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table if not exists public.driver_wallet ( id uuid primary key default gen_random_uuid(), driver_id uuid not null unique references public.drivers(id) on delete cascade, balance numeric(12,2) not null default 0, last_updated timestamptz not null default now() ); create table if not exists public.driver_cars ( id uuid primary key default gen_random_uuid(), driver_id uuid not null references public.drivers(id) on delete cascade, car_type text not null, plate_number text unique, seat_count integer check (seat_count > 0), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table if not exists public.driver_trips ( id uuid primary key default gen_random_uuid(), driver_id uuid not null references public.drivers(id) on delete cascade, car_id uuid references public.driver_cars(id) on delete set null, departure text not null, destination text not null, departure_date date not null, departure_time text not null check (departure_time in ('morning', 'noon', 'night')), available_seats integer not null check (available_seats >= 0), total_seats integer not null check (total_seats > 0), price numeric(12,2) not null check (price >= 0), status text not null default 'active' check (status in ('active', 'cancelled', 'completed')), created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint driver_trips_available_not_over_total check (available_seats <= total_seats) ); create table if not exists public.messages ( id uuid primary key default gen_random_uuid(), customer_id uuid not null references public.customers(id) on delete cascade, sender_type text not null check (sender_type in ('customer', 'assistant', 'driver', 'system')), message text not null, whatsapp_message_id text unique, metadata jsonb not null default '{}'::jsonb, created_at timestamptz not null default now() ); create table if not exists public.booking_leads ( id uuid primary key default gen_random_uuid(), customer_id uuid not null references public.customers(id) on delete cascade, trip_id uuid not null references public.driver_trips(id) on delete restrict, requested_seats integer not null check (requested_seats > 0), status text not null default 'pending' check (status in ('pending', 'confirmed', 'cancelled')), notes text, driver_notification_status text not null default 'not_sent' check (driver_notification_status in ('not_sent', 'sent', 'failed')), 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_messages_customer_created_at on public.messages(customer_id, created_at desc); create index if not exists idx_driver_trips_active_route on public.driver_trips(status, departure, destination, departure_date, departure_time); create index if not exists idx_booking_leads_customer on public.booking_leads(customer_id, created_at desc); create index if not exists idx_booking_leads_trip on public.booking_leads(trip_id, created_at desc); drop trigger if exists set_customers_updated_at on public.customers; create trigger set_customers_updated_at before update on public.customers for each row execute function public.set_updated_at(); drop trigger if exists set_drivers_updated_at on public.drivers; create trigger set_drivers_updated_at before update on public.drivers for each row execute function public.set_updated_at(); drop trigger if exists set_driver_cars_updated_at on public.driver_cars; create trigger set_driver_cars_updated_at before update on public.driver_cars for each row execute function public.set_updated_at(); drop trigger if exists set_driver_trips_updated_at on public.driver_trips; create trigger set_driver_trips_updated_at before update on public.driver_trips for each row execute function public.set_updated_at(); drop trigger if exists set_booking_leads_updated_at on public.booking_leads; create trigger set_booking_leads_updated_at before update on public.booking_leads for each row execute function public.set_updated_at(); alter table public.customers enable row level security; alter table public.messages enable row level security; alter table public.drivers enable row level security; alter table public.driver_wallet enable row level security; alter table public.driver_cars enable row level security; alter table public.driver_trips enable row level security; alter table public.booking_leads enable row level security; grant usage on schema public to service_role; grant all on all tables in schema public to service_role; grant all on all routines in schema public to service_role; grant all on all sequences in schema public to service_role;