-- backend/db/schema.sql - Velocity PostgreSQL schema
-- Omnichannel Intelligence Graph for The Sentinel.
--
-- Run via:
--   psql -U velocity_user -d velocity_db -f schema.sql
--
-- Or via Alembic (preferred - see backend/alembic/).

-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- ────────────────────────────────────────────────────────────────────────────
-- ENUM TYPES
-- ────────────────────────────────────────────────────────────────────────────

DO $$ BEGIN
  CREATE TYPE role_enum AS ENUM (
    'ADMIN',
    'SALES_DIRECTOR',
    'SENIOR_BROKER',
    'JUNIOR_BROKER'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE source_enum AS ENUM ('whatsapp', 'website', 'walkin');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE lead_status_enum AS ENUM ('new', 'engaged', 'qualified', 'hot', 'closed');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE qualification_enum AS ENUM ('whale', 'potential', 'tire_kicker');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE log_event_enum AS ENUM (
    'CALL_LOGGED',
    'ASSET_VIEWED',
    'SENTIMENT_SPIKE',
    'QD_UPDATED',
    'LEAD_TAGGED',
    'WS_ASSET_OPENED'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

-- ────────────────────────────────────────────────────────────────────────────
-- TABLE: users_and_roles  (Manual RBAC backbone)
-- ────────────────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS users_and_roles (
  id            UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  email         TEXT          UNIQUE NOT NULL,
  password_hash TEXT          NOT NULL,
  role          role_enum     NOT NULL DEFAULT 'JUNIOR_BROKER',
  tenant_id     TEXT          NOT NULL DEFAULT 'tenant_velocity',
  full_name     TEXT,
  avatar_url    TEXT,
  is_active     BOOLEAN       NOT NULL DEFAULT TRUE,
  last_login    TIMESTAMPTZ,
  created_at    TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

-- Index for login lookups
CREATE INDEX IF NOT EXISTS idx_users_email ON users_and_roles (email);
CREATE INDEX IF NOT EXISTS idx_users_tenant_active ON users_and_roles (tenant_id, is_active);

-- ────────────────────────────────────────────────────────────────────────────
-- TABLE: leads_intelligence  (CRM core with QD scoring)
-- ────────────────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS leads_intelligence (
  id                     UUID               PRIMARY KEY DEFAULT gen_random_uuid(),
  name                   TEXT               NOT NULL,
  phone                  TEXT,
  email                  TEXT,
  source                 source_enum        NOT NULL,
  status                 lead_status_enum   NOT NULL DEFAULT 'new',
  qualification          qualification_enum,
  budget                 TEXT,
  interest               TEXT,
  -- Quantum Dynamics Score: 1-100, updated in real-time by NemoClaw
  quantum_dynamics_score INTEGER            CHECK (quantum_dynamics_score BETWEEN 1 AND 100),
  -- Polymorphic CRM intelligence tags e.g. ['HNI', 'NRI', 'Hot Lead']
  tags                   TEXT[]             NOT NULL DEFAULT '{}',
  assigned_to            UUID               REFERENCES users_and_roles(id) ON DELETE SET NULL,
  last_message           TEXT,
  last_active            TIMESTAMPTZ        NOT NULL DEFAULT NOW(),
  created_at             TIMESTAMPTZ        NOT NULL DEFAULT NOW(),
  updated_at             TIMESTAMPTZ        NOT NULL DEFAULT NOW()
);

-- Auto-update updated_at on every modification
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_leads_updated_at ON leads_intelligence;
CREATE TRIGGER trg_leads_updated_at
  BEFORE UPDATE ON leads_intelligence
  FOR EACH ROW EXECUTE FUNCTION set_updated_at();

CREATE INDEX IF NOT EXISTS idx_leads_status ON leads_intelligence (status);
CREATE INDEX IF NOT EXISTS idx_leads_assigned ON leads_intelligence (assigned_to);
-- GIN index for efficient tag array queries (e.g. WHERE 'HNI' = ANY(tags))
CREATE INDEX IF NOT EXISTS idx_leads_tags ON leads_intelligence USING GIN (tags);

-- ────────────────────────────────────────────────────────────────────────────
-- TABLE: velocity_vault_assets  (File Tracking Engine)
-- ────────────────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS velocity_vault_assets (
  id            UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  asset_name    TEXT          NOT NULL,
  asset_type    TEXT          NOT NULL,   -- 'pdf', 'image', 'video'
  storage_path  TEXT          NOT NULL,   -- relative to /opt/dlami/nvme/assets/
  -- Unique cryptographic string for every share instance
  tracking_hash VARCHAR(64)   UNIQUE NOT NULL,
  lead_id       UUID          REFERENCES leads_intelligence(id) ON DELETE CASCADE,
  created_by    UUID          REFERENCES users_and_roles(id),
  -- Array of open timestamps; one entry appended per distinct open event
  opened_at     TIMESTAMPTZ[] NOT NULL DEFAULT '{}',
  created_at    TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_vault_hash ON velocity_vault_assets (tracking_hash);
CREATE INDEX IF NOT EXISTS idx_vault_lead ON velocity_vault_assets (lead_id);

-- ────────────────────────────────────────────────────────────────────────────
-- TABLE: omnichannel_logs  (Polymorphic event ingestion + sentimental history)
-- ────────────────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS omnichannel_logs (
  id                UUID            PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type        log_event_enum  NOT NULL,
  lead_id           UUID            REFERENCES leads_intelligence(id) ON DELETE CASCADE,
  -- JSONB payload — schema varies by event_type:
  --   SENTIMENT_SPIKE: {blend_shapes, qd_before, qd_after}
  --   WS_ASSET_OPENED: {ip, user_agent, tracking_hash}
  --   QD_UPDATED: {qd_score, reasoning, confidence}
  --   LEAD_TAGGED: {tags_added, tags_removed}
  payload           JSONB           NOT NULL DEFAULT '{}',
  -- For MediaPipe-correlated entries: exact ms offset in the stimulus video
  video_timestamp_ms BIGINT,
  created_at        TIMESTAMPTZ     NOT NULL DEFAULT NOW()
);

-- Composite index supporting time-series sentiment history queries
CREATE INDEX IF NOT EXISTS idx_logs_lead_type_time
  ON omnichannel_logs (lead_id, event_type, created_at DESC);

-- Partial index for fast SENTIMENT_SPIKE lookups
CREATE INDEX IF NOT EXISTS idx_logs_sentiment_spikes
  ON omnichannel_logs (lead_id, created_at DESC)
  WHERE event_type = 'SENTIMENT_SPIKE';

-- ────────────────────────────────────────────────────────────────────────────
-- TABLE: consent_log  (GDPR biometric consent tracking)
-- ────────────────────────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS consent_log (
  id           UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  lead_id      UUID        REFERENCES leads_intelligence(id) ON DELETE CASCADE,
  consented_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  ip_address   TEXT,
  user_agent   TEXT,
  -- 'granted' or 'revoked'
  action       TEXT        NOT NULL DEFAULT 'granted'
);

CREATE INDEX IF NOT EXISTS idx_consent_lead ON consent_log (lead_id, consented_at DESC);
