create extension if not exists pgcrypto;
create extension if not exists citext;

create table if not exists roles (
    id uuid primary key default gen_random_uuid(),
    name text unique not null,
    description text not null default '',
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now()
);

create table if not exists users (
    id uuid primary key default gen_random_uuid(),
    role_id uuid not null references roles(id),
    username citext unique not null,
    display_name text not null,
    email citext unique,
    password_hash text not null,
    is_active boolean not null default true,
    last_login_at timestamptz,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create table if not exists sessions (
    id uuid primary key default gen_random_uuid(),
    user_id uuid not null references users(id),
    ip_address inet,
    user_agent text,
    last_seen_at timestamptz not null default now(),
    expires_at timestamptz not null,
    created_at timestamptz not null default now(),
    revoked_at timestamptz
);

create table if not exists refresh_tokens (
    id uuid primary key default gen_random_uuid(),
    session_id uuid not null references sessions(id),
    user_id uuid not null references users(id),
    token_hash text not null,
    expires_at timestamptz not null,
    created_at timestamptz not null default now(),
    revoked_at timestamptz
);

create table if not exists gateways (
    id uuid primary key default gen_random_uuid(),
    name text unique not null,
    endpoint text not null,
    public_key text not null,
    listen_port integer not null,
    vpn_cidr cidr not null,
    dns_servers text[] not null default '{}',
    is_active boolean not null default true,
    last_sync_at timestamptz,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create table if not exists devices (
    id uuid primary key default gen_random_uuid(),
    user_id uuid not null references users(id),
    gateway_id uuid references gateways(id),
    name text not null,
    platform text not null,
    os_version text not null default '',
    app_version text not null default '',
    device_fingerprint text not null,
    public_key text not null,
    status text not null default 'active',
    last_seen_at timestamptz,
    last_connected_at timestamptz,
    approved_at timestamptz,
    revoked_at timestamptz,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create unique index if not exists idx_devices_user_fingerprint on devices(user_id, device_fingerprint) where deleted_at is null;

create table if not exists wireguard_peers (
    id uuid primary key default gen_random_uuid(),
    device_id uuid not null references devices(id),
    gateway_id uuid not null references gateways(id),
    public_key text unique not null,
    assigned_ip inet not null,
    preshared_key_ciphertext text,
    allowed_ips cidr[] not null default '{}',
    dns_servers text[] not null default '{}',
    profile_revision integer not null default 1,
    last_profile_issued_at timestamptz,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create table if not exists ip_allocations (
    id uuid primary key default gen_random_uuid(),
    gateway_id uuid not null references gateways(id),
    device_id uuid references devices(id),
    address inet not null,
    status text not null default 'allocated',
    allocated_at timestamptz not null default now(),
    released_at timestamptz,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now()
);

create unique index if not exists idx_ip_allocations_gateway_address on ip_allocations(gateway_id, address);
create unique index if not exists idx_ip_allocations_device_active on ip_allocations(device_id) where status = 'allocated';

create table if not exists policies (
    id uuid primary key default gen_random_uuid(),
    name text unique not null,
    description text not null default '',
    priority integer not null default 100,
    effect text not null default 'allow',
    is_active boolean not null default true,
    full_tunnel boolean not null default false,
    created_by uuid references users(id),
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create table if not exists policy_targets (
    id uuid primary key default gen_random_uuid(),
    policy_id uuid not null references policies(id),
    target_type text not null,
    target_id uuid not null,
    created_at timestamptz not null default now()
);

create table if not exists policy_destinations (
    id uuid primary key default gen_random_uuid(),
    policy_id uuid not null references policies(id),
    destination cidr not null,
    description text not null default '',
    created_at timestamptz not null default now()
);

create table if not exists groups (
    id uuid primary key default gen_random_uuid(),
    name text unique not null,
    description text not null default '',
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    deleted_at timestamptz
);

create table if not exists group_memberships (
    id uuid primary key default gen_random_uuid(),
    group_id uuid not null references groups(id),
    user_id uuid not null references users(id),
    created_at timestamptz not null default now()
);

create table if not exists audit_logs (
    id uuid primary key default gen_random_uuid(),
    actor_user_id uuid references users(id),
    actor_device_id uuid references devices(id),
    event_type text not null,
    entity_type text not null,
    entity_id uuid,
    status text not null,
    ip_address inet,
    message text not null,
    metadata jsonb not null default '{}'::jsonb,
    created_at timestamptz not null default now()
);

create table if not exists settings (
    id uuid primary key default gen_random_uuid(),
    category text not null,
    key text not null,
    value jsonb not null,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    unique(category, key)
);
