-- =============================================================================
-- schema_crm_canonical.sql
-- Project Velocity — Canonical CRM and Platform Schema
-- =============================================================================
-- Covers: crm_*, intel_*, inventory_*, workflow_* canonical domains
-- as specified in Doc 09: Database Schema and Root API Spec
-- and Doc 07: Contracts and Schema Blueprint
--
-- Run AFTER schema.sql and schema_addendum.sql
-- psql -U velocity_user -d velocity_db -f schema_crm_canonical.sql
--
-- Existing tables: users_and_roles, leads_intelligence, velocity_vault_assets,
--                  omnichannel_logs, consent_log, video_scene_maps,
--                  perception_sessions, cctv_events, leads, chat_logs
-- These are treated as legacy feeders per the reconciliation matrix.
-- =============================================================================

CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";

-- ─────────────────────────────────────────────────────────────────────────────
-- ENUM TYPES — Canonical Domain
-- ─────────────────────────────────────────────────────────────────────────────

DO $$ BEGIN
  CREATE TYPE crm_lead_status AS ENUM (
    'new', 'contacted', 'qualified', 'site_visit_scheduled', 'site_visited',
    'negotiation', 'booking_initiated', 'booked', 'lost', 'dormant'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE crm_opportunity_stage AS ENUM (
    'prospect', 'qualified', 'proposal', 'site_visit', 'negotiation',
    'booking', 'agreement', 'closed_won', 'closed_lost'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE crm_account_type AS ENUM (
    'individual', 'company', 'broker', 'developer', 'referral_partner', 'nri_family'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE crm_relationship_type AS ENUM (
    'spouse', 'parent', 'sibling', 'business_partner', 'broker_referral',
    'co_buyer', 'family_member', 'advisor'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE intel_channel AS ENUM (
    'whatsapp', 'phone', 'email', 'site_visit', 'office_meeting',
    'video_call', 'cctv', 'perception_session', 'system'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE intel_call_direction AS ENUM ('inbound', 'outbound');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE wf_status AS ENUM (
    'pending', 'review_required', 'approved', 'rejected', 'executed', 'failed', 'cancelled'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

DO $$ BEGIN
  CREATE TYPE import_lifecycle AS ENUM (
    'uploaded', 'parsed', 'mapped', 'proposed', 'approved', 'committed', 'failed'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

-- ─────────────────────────────────────────────────────────────────────────────
-- SECTION 1: CRM CORE DOMAIN (crm_*)
-- ─────────────────────────────────────────────────────────────────────────────

-- TABLE: crm_people
-- Purpose: canonical person-level contact identity
CREATE TABLE IF NOT EXISTS crm_people (
  person_id         UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  full_name         TEXT          NOT NULL,
  primary_email     TEXT,
  primary_phone     TEXT,
  secondary_phone   TEXT,
  linkedin_url      TEXT,
  city              TEXT,
  nationality       TEXT,
  buyer_type        TEXT,         -- high_intent, slow_burn_investor, nri, etc.
  persona_labels    JSONB         NOT NULL DEFAULT '[]'::jsonb,
  source_confidence FLOAT         CHECK (source_confidence BETWEEN 0.0 AND 1.0),
  -- Legacy feeder references (migration linkage)
  legacy_lead_id    TEXT,         -- links to old leads.id
  legacy_li_id      UUID,         -- links to leads_intelligence.id
  -- Metadata
  metadata_json     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_crm_people_email ON crm_people (primary_email);
CREATE INDEX IF NOT EXISTS idx_crm_people_phone ON crm_people (primary_phone);
CREATE INDEX IF NOT EXISTS idx_crm_people_name_trgm ON crm_people USING GIN (full_name gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_crm_people_buyer_type ON crm_people (buyer_type);

-- TABLE: crm_accounts
-- Purpose: company, employer, brokerage, or client organization
CREATE TABLE IF NOT EXISTS crm_accounts (
  account_id        UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  account_name      TEXT          NOT NULL,
  parent_account_id UUID          REFERENCES crm_accounts(account_id) ON DELETE SET NULL,
  account_type      crm_account_type NOT NULL DEFAULT 'company',
  industry          TEXT,
  location_ref      TEXT,
  website           TEXT,
  metadata_json     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_crm_accounts_name ON crm_accounts (account_name);
CREATE INDEX IF NOT EXISTS idx_crm_accounts_type ON crm_accounts (account_type);

-- TABLE: crm_households
-- Purpose: family or co-buyer unit grouping
CREATE TABLE IF NOT EXISTS crm_households (
  household_id      UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  household_name    TEXT          NOT NULL,
  primary_person_id UUID          REFERENCES crm_people(person_id) ON DELETE SET NULL,
  notes             TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

-- TABLE: crm_relationships
-- Purpose: person-to-person relationship graph
CREATE TABLE IF NOT EXISTS crm_relationships (
  relationship_id   UUID                    PRIMARY KEY DEFAULT gen_random_uuid(),
  person_a_id       UUID                    NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  person_b_id       UUID                    NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  relationship_type crm_relationship_type   NOT NULL,
  household_id      UUID                    REFERENCES crm_households(household_id) ON DELETE SET NULL,
  notes             TEXT,
  created_at        TIMESTAMPTZ             NOT NULL DEFAULT NOW(),
  UNIQUE (person_a_id, person_b_id, relationship_type)
);

CREATE INDEX IF NOT EXISTS idx_crm_rel_a ON crm_relationships (person_a_id);
CREATE INDEX IF NOT EXISTS idx_crm_rel_b ON crm_relationships (person_b_id);

-- TABLE: crm_leads
-- Purpose: funnel-stage commercial qualification layer
CREATE TABLE IF NOT EXISTS crm_leads (
  lead_id           UUID              PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID              NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  account_id        UUID              REFERENCES crm_accounts(account_id) ON DELETE SET NULL,
  source_system     TEXT              DEFAULT 'velocity',
  status            crm_lead_status   NOT NULL DEFAULT 'new',
  budget_band       TEXT,
  urgency           TEXT,             -- low, medium, high, critical
  financing_posture TEXT,             -- cash, loan, nri_remittance, emi
  timeline_to_decision TEXT,
  objections        JSONB             NOT NULL DEFAULT '[]'::jsonb,
  motivations       JSONB             NOT NULL DEFAULT '[]'::jsonb,
  assigned_user_id  UUID              REFERENCES users_and_roles(id) ON DELETE SET NULL,
  -- Legacy feeder
  legacy_lead_id    TEXT,
  metadata_json     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_crm_leads_person ON crm_leads (person_id);
CREATE INDEX IF NOT EXISTS idx_crm_leads_status ON crm_leads (status);
CREATE INDEX IF NOT EXISTS idx_crm_leads_assigned ON crm_leads (assigned_user_id);
CREATE INDEX IF NOT EXISTS idx_crm_leads_source ON crm_leads (source_system);

-- TABLE: crm_opportunities
-- Purpose: deal pipeline objects
CREATE TABLE IF NOT EXISTS crm_opportunities (
  opportunity_id      UUID                    PRIMARY KEY DEFAULT gen_random_uuid(),
  lead_id             UUID                    NOT NULL REFERENCES crm_leads(lead_id) ON DELETE CASCADE,
  project_id          UUID,                   -- references inventory_projects
  unit_id             UUID,                   -- references inventory_units
  stage               crm_opportunity_stage   NOT NULL DEFAULT 'prospect',
  value               DECIMAL(15, 2),
  probability         INTEGER                 CHECK (probability BETWEEN 0 AND 100),
  expected_close_date DATE,
  next_action         TEXT,
  notes               TEXT,
  metadata_json       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_crm_opp_lead ON crm_opportunities (lead_id);
CREATE INDEX IF NOT EXISTS idx_crm_opp_stage ON crm_opportunities (stage);
CREATE INDEX IF NOT EXISTS idx_crm_opp_project ON crm_opportunities (project_id);

-- TABLE: crm_property_interests
-- Purpose: project and unit interest linking per client
CREATE TABLE IF NOT EXISTS crm_property_interests (
  interest_id       UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  lead_id           UUID          REFERENCES crm_leads(lead_id) ON DELETE SET NULL,
  project_id        UUID,
  project_name      TEXT          NOT NULL,
  unit_preference   TEXT,
  configuration     TEXT,         -- 2BHK, 3BHK, Penthouse, etc.
  budget_min        DECIMAL(15, 2),
  budget_max        DECIMAL(15, 2),
  priority          INTEGER       DEFAULT 1,  -- 1 = primary, 2 = secondary
  notes             TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

ALTER TABLE crm_property_interests
  ADD COLUMN IF NOT EXISTS metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb;

CREATE INDEX IF NOT EXISTS idx_crm_pi_person ON crm_property_interests (person_id);
CREATE INDEX IF NOT EXISTS idx_crm_pi_project ON crm_property_interests (project_id);

-- TABLE: crm_stage_history
-- Purpose: canonical audit trail of lead stage transitions
CREATE TABLE IF NOT EXISTS crm_stage_history (
  history_id        UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  lead_id           UUID          NOT NULL REFERENCES crm_leads(lead_id) ON DELETE CASCADE,
  from_status       TEXT,
  to_status         TEXT          NOT NULL,
  changed_by        UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  changed_by_type   TEXT          DEFAULT 'human',  -- human, ai, system
  notes             TEXT,
  happened_at       TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_crm_stage_lead ON crm_stage_history (lead_id, happened_at DESC);

-- ─────────────────────────────────────────────────────────────────────────────
-- SECTION 2: INTERACTION AND EVIDENCE GRAPH (intel_*)
-- ─────────────────────────────────────────────────────────────────────────────

-- TABLE: intel_interactions
-- Purpose: umbrella interaction event record
CREATE TABLE IF NOT EXISTS intel_interactions (
  interaction_id    UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  lead_id           UUID          REFERENCES crm_leads(lead_id) ON DELETE SET NULL,
  channel           intel_channel NOT NULL,
  interaction_type  TEXT          NOT NULL,  -- message, call, visit, email, note
  happened_at       TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  summary           TEXT,
  source_ref        TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_intel_int_person ON intel_interactions (person_id, happened_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_int_lead ON intel_interactions (lead_id, happened_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_int_channel ON intel_interactions (channel);

-- TABLE: intel_messages
-- Purpose: text-level message records (WhatsApp, chat)
CREATE TABLE IF NOT EXISTS intel_messages (
  message_id        UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  interaction_id    UUID          NOT NULL REFERENCES intel_interactions(interaction_id) ON DELETE CASCADE,
  thread_id         UUID,
  sender_role       TEXT          NOT NULL,  -- lead, broker, system, oracle
  sender_name       TEXT,
  message_text      TEXT          NOT NULL,
  delivered_at      TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_msg_interaction ON intel_messages (interaction_id, delivered_at DESC);

-- TABLE: intel_whatsapp_threads
-- Purpose: WhatsApp thread-level summaries
CREATE TABLE IF NOT EXISTS intel_whatsapp_threads (
  thread_id         UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  lead_id           UUID          REFERENCES crm_leads(lead_id) ON DELETE SET NULL,
  phone_number      TEXT,
  thread_summary    TEXT,
  message_count     INTEGER       DEFAULT 0,
  last_message_at   TIMESTAMPTZ,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_intel_wa_person ON intel_whatsapp_threads (person_id);

-- TABLE: intel_calls
-- Purpose: voice call records
CREATE TABLE IF NOT EXISTS intel_calls (
  call_id           UUID                  PRIMARY KEY DEFAULT gen_random_uuid(),
  interaction_id    UUID                  NOT NULL REFERENCES intel_interactions(interaction_id) ON DELETE CASCADE,
  call_direction    intel_call_direction  NOT NULL DEFAULT 'outbound',
  duration_seconds  INTEGER,
  recording_ref     TEXT,             -- storage path or URL to recording
  transcript_ref    TEXT,             -- path to transcript JSON
  call_outcome      TEXT,             -- connected, no_answer, voicemail, dropped
  called_number     TEXT,
  metadata_json     JSONB             NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_call_interaction ON intel_calls (interaction_id);

-- TABLE: intel_transcripts
-- Purpose: transcript and speaker segmentation storage
CREATE TABLE IF NOT EXISTS intel_transcripts (
  transcript_id       UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  call_id             UUID          REFERENCES intel_calls(call_id) ON DELETE SET NULL,
  interaction_id      UUID          REFERENCES intel_interactions(interaction_id) ON DELETE SET NULL,
  language            TEXT          DEFAULT 'en',
  full_text           TEXT,
  speaker_segments_json JSONB       NOT NULL DEFAULT '[]'::jsonb,
  confidence          FLOAT         CHECK (confidence BETWEEN 0.0 AND 1.0),
  word_count          INTEGER,
  metadata_json       JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at          TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_intel_transcript_call ON intel_transcripts (call_id);
CREATE INDEX IF NOT EXISTS idx_intel_transcript_interaction ON intel_transcripts (interaction_id);

-- TABLE: intel_emails
-- Purpose: email thread records
CREATE TABLE IF NOT EXISTS intel_emails (
  email_id          UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  interaction_id    UUID          NOT NULL REFERENCES intel_interactions(interaction_id) ON DELETE CASCADE,
  from_address      TEXT,
  to_addresses      JSONB         NOT NULL DEFAULT '[]'::jsonb,
  subject           TEXT,
  body_text         TEXT,
  has_attachments   BOOLEAN       DEFAULT FALSE,
  sent_at           TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_email_interaction ON intel_emails (interaction_id);

-- TABLE: intel_visits
-- Purpose: site visit and meeting records
CREATE TABLE IF NOT EXISTS intel_visits (
  visit_id          UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  lead_id           UUID          REFERENCES crm_leads(lead_id) ON DELETE SET NULL,
  project_id        UUID,
  project_name      TEXT,
  unit_id           UUID,
  visited_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  visit_notes       TEXT,
  host_user_id      UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  revisit_intent    TEXT,         -- very_likely, likely, uncertain, unlikely
  cctv_session_ref  TEXT,
  perception_session_ref TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_visits_person ON intel_visits (person_id, visited_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_visits_project ON intel_visits (project_id);

-- TABLE: intel_reminders
-- Purpose: reminders and follow-up task chains
CREATE TABLE IF NOT EXISTS intel_reminders (
  reminder_id       UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  lead_id           UUID          REFERENCES crm_leads(lead_id) ON DELETE SET NULL,
  opportunity_id    UUID          REFERENCES crm_opportunities(opportunity_id) ON DELETE SET NULL,
  reminder_type     TEXT          NOT NULL,  -- call_back, follow_up, site_visit, document, negotiation
  title             TEXT          NOT NULL,
  notes             TEXT,
  due_at            TIMESTAMPTZ,
  completed_at      TIMESTAMPTZ,
  status            TEXT          NOT NULL DEFAULT 'pending',  -- pending, done, snoozed, cancelled
  assigned_to       UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  created_by_type   TEXT          DEFAULT 'human',  -- human, ai, system
  priority          TEXT          DEFAULT 'normal',   -- low, normal, high, urgent
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_intel_reminder_person ON intel_reminders (person_id, due_at);
CREATE INDEX IF NOT EXISTS idx_intel_reminder_status ON intel_reminders (status, due_at);
CREATE INDEX IF NOT EXISTS idx_intel_reminder_assigned ON intel_reminders (assigned_to, due_at);

-- TABLE: intel_qd_scores
-- Purpose: latest meaningful QD summary by client
CREATE TABLE IF NOT EXISTS intel_qd_scores (
  qd_id             UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  score_type        TEXT          NOT NULL,  -- intent_score, urgency_score, engagement_score
  current_value     FLOAT         NOT NULL CHECK (current_value BETWEEN 0.0 AND 1.0),
  computed_at       TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  evidence_refs_json JSONB        NOT NULL DEFAULT '[]'::jsonb,
  reasoning         TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  UNIQUE (person_id, score_type)
);

CREATE INDEX IF NOT EXISTS idx_intel_qd_person ON intel_qd_scores (person_id);

-- TABLE: intel_qd_timeseries
-- Purpose: time-series QD propagation and shifts
CREATE TABLE IF NOT EXISTS intel_qd_timeseries (
  timeseries_id     UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          NOT NULL REFERENCES crm_people(person_id) ON DELETE CASCADE,
  score_type        TEXT          NOT NULL,
  signal_source     TEXT,
  timestamp         TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  value             FLOAT         NOT NULL CHECK (value BETWEEN 0.0 AND 1.0),
  delta             FLOAT,
  evidence_ref      TEXT,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_qd_ts_person ON intel_qd_timeseries (person_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_intel_qd_ts_type ON intel_qd_timeseries (score_type, timestamp DESC);

-- TABLE: intel_vehicle_events
-- Purpose: number-plate and vehicle detection events
CREATE TABLE IF NOT EXISTS intel_vehicle_events (
  event_id          UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          REFERENCES crm_people(person_id) ON DELETE SET NULL,
  visit_id          UUID          REFERENCES intel_visits(visit_id) ON DELETE SET NULL,
  zone              TEXT,
  license_plate_hash TEXT,        -- hashed for privacy
  vehicle_class     TEXT,         -- luxury, standard, unknown
  wealth_indicator  TEXT,         -- HNI, standard, unknown
  cctv_ref          TEXT,
  captured_at       TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_vehicle_person ON intel_vehicle_events (person_id);

-- TABLE: intel_perception_events
-- Purpose: behavioral and dwell-time intelligence from perception sessions
CREATE TABLE IF NOT EXISTS intel_perception_events (
  perception_id     UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          REFERENCES crm_people(person_id) ON DELETE SET NULL,
  visit_id          UUID          REFERENCES intel_visits(visit_id) ON DELETE SET NULL,
  session_ref       TEXT,         -- perception_sessions.id linkage
  event_type        TEXT          NOT NULL,  -- room_dwell, engagement_spike, exit
  rooms_visited     JSONB         NOT NULL DEFAULT '[]'::jsonb,
  dwell_time_seconds INTEGER,
  engagement_score  FLOAT         CHECK (engagement_score BETWEEN 0.0 AND 1.0),
  camera_id         TEXT,
  media_ref         TEXT,
  happened_at       TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_perception_person ON intel_perception_events (person_id);

-- TABLE: intel_cctv_links
-- Purpose: CCTV evidence references linked to client/visit contexts
CREATE TABLE IF NOT EXISTS intel_cctv_links (
  link_id           UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  person_id         UUID          REFERENCES crm_people(person_id) ON DELETE SET NULL,
  visit_id          UUID          REFERENCES intel_visits(visit_id) ON DELETE SET NULL,
  cctv_event_id     UUID          REFERENCES cctv_events(id) ON DELETE SET NULL,
  clip_ref          TEXT,
  camera_zone       TEXT,
  confidence        FLOAT         CHECK (confidence BETWEEN 0.0 AND 1.0),
  linked_at         TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_intel_cctv_person ON intel_cctv_links (person_id);

-- ─────────────────────────────────────────────────────────────────────────────
-- SECTION 3: INVENTORY DOMAIN (inventory_*)
-- ─────────────────────────────────────────────────────────────────────────────

-- TABLE: inventory_projects
-- Purpose: project-level inventory master
CREATE TABLE IF NOT EXISTS inventory_projects (
  project_id        UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  project_name      TEXT          NOT NULL UNIQUE,
  developer_name    TEXT          NOT NULL,
  city              TEXT          NOT NULL DEFAULT 'Kolkata',
  micro_market      TEXT,
  address           TEXT,
  total_units       INTEGER,
  rera_number       TEXT,
  project_status    TEXT          DEFAULT 'active',  -- active, sold_out, upcoming
  launch_date       DATE,
  possession_date   DATE,
  location_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  amenities_json    JSONB         NOT NULL DEFAULT '[]'::jsonb,
  metadata_json     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_inv_projects_name ON inventory_projects (project_name);
CREATE INDEX IF NOT EXISTS idx_inv_projects_market ON inventory_projects (micro_market);

-- TABLE: inventory_units
-- Purpose: unit-level availability and attributes
CREATE TABLE IF NOT EXISTS inventory_units (
  unit_id           UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id        UUID          NOT NULL REFERENCES inventory_projects(project_id) ON DELETE CASCADE,
  unit_label        TEXT          NOT NULL,
  configuration     TEXT          NOT NULL,  -- 2BHK, 3BHK, Penthouse, etc.
  area_sqft         DECIMAL(10, 2),
  price_current     DECIMAL(15, 2),
  price_psf         DECIMAL(10, 2),
  status            TEXT          NOT NULL DEFAULT 'available',  -- available, reserved, sold, hold
  floor             INTEGER,
  tower             TEXT,
  facing            TEXT,
  has_attached_amenities JSONB    NOT NULL DEFAULT '[]'::jsonb,
  metadata_json     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  updated_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  UNIQUE (project_id, unit_label)
);

CREATE INDEX IF NOT EXISTS idx_inv_units_project ON inventory_units (project_id);
CREATE INDEX IF NOT EXISTS idx_inv_units_status ON inventory_units (status);
CREATE INDEX IF NOT EXISTS idx_inv_units_config ON inventory_units (configuration);

-- TABLE: inventory_import_jobs
-- Purpose: track inventory CSV import operations
CREATE TABLE IF NOT EXISTS inventory_import_jobs (
  job_id            UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id        UUID          REFERENCES inventory_projects(project_id) ON DELETE SET NULL,
  filename          TEXT          NOT NULL,
  row_count         INTEGER,
  imported_by       UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  status            TEXT          NOT NULL DEFAULT 'pending',
  errors_json       JSONB         NOT NULL DEFAULT '[]'::jsonb,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  completed_at      TIMESTAMPTZ
);

-- ─────────────────────────────────────────────────────────────────────────────
-- SECTION 4: AI WORKFLOW AND GOVERNANCE (workflow_*)
-- ─────────────────────────────────────────────────────────────────────────────

-- TABLE: workflow_actions
-- Purpose: track proposed AI/human actions before approval
CREATE TABLE IF NOT EXISTS workflow_actions (
  action_id         UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  action_type       TEXT          NOT NULL,  -- import_review, merge_proposal, writeback, enrichment
  target_domain     TEXT          NOT NULL,  -- crm, intel, inventory
  target_entity_ref TEXT,
  proposal_payload  JSONB         NOT NULL DEFAULT '{}'::jsonb,
  reasoning_summary TEXT,
  evidence_refs     JSONB         NOT NULL DEFAULT '[]'::jsonb,
  confidence        FLOAT         CHECK (confidence BETWEEN 0.0 AND 1.0),
  status            wf_status     NOT NULL DEFAULT 'pending',
  approval_required BOOLEAN       NOT NULL DEFAULT TRUE,
  created_by_agent  TEXT,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  updated_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_wf_actions_status ON workflow_actions (status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_wf_actions_domain ON workflow_actions (target_domain);

-- TABLE: workflow_approvals
-- Purpose: explicit human review decisions
CREATE TABLE IF NOT EXISTS workflow_approvals (
  decision_id       UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  action_id         UUID          NOT NULL REFERENCES workflow_actions(action_id) ON DELETE CASCADE,
  reviewer_id       UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  decision          TEXT          NOT NULL,  -- approved, rejected, needs_more_info
  decision_notes    TEXT,
  decided_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_wf_approvals_action ON workflow_approvals (action_id);

-- TABLE: workflow_writebacks
-- Purpose: track AI-suggested and approved canonical mutations
CREATE TABLE IF NOT EXISTS workflow_writebacks (
  writeback_id      UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  action_id         UUID          REFERENCES workflow_actions(action_id) ON DELETE SET NULL,
  approval_id       UUID          REFERENCES workflow_approvals(decision_id) ON DELETE SET NULL,
  target_domain     TEXT          NOT NULL,
  target_entity_ref TEXT          NOT NULL,
  change_payload    JSONB         NOT NULL DEFAULT '{}'::jsonb,
  status            wf_status     NOT NULL DEFAULT 'pending',
  approved_by       UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  executed_at       TIMESTAMPTZ,
  error_detail      TEXT,
  created_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_wf_wb_status ON workflow_writebacks (status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_wf_wb_domain ON workflow_writebacks (target_domain);

-- TABLE: workflow_import_batches
-- Purpose: CRM import batch lifecycle tracking (RawImportBatch contract)
CREATE TABLE IF NOT EXISTS workflow_import_batches (
  batch_id          UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  source_system     TEXT          NOT NULL,  -- csv_upload, salesforce, hubspot, manual
  uploaded_filename TEXT,
  mime_type         TEXT          DEFAULT 'text/csv',
  storage_ref       TEXT,
  row_count         INTEGER,
  mapped_count      INTEGER       DEFAULT 0,
  unresolved_count  INTEGER       DEFAULT 0,
  canonical_count   INTEGER       DEFAULT 0,
  uploaded_by       UUID          REFERENCES users_and_roles(id) ON DELETE SET NULL,
  lifecycle         import_lifecycle NOT NULL DEFAULT 'uploaded',
  mapping_manifest  JSONB         NOT NULL DEFAULT '{}'::jsonb,
  errors_json       JSONB         NOT NULL DEFAULT '[]'::jsonb,
  metadata_json     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_wf_import_lifecycle ON workflow_import_batches (lifecycle, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_wf_import_user ON workflow_import_batches (uploaded_by);

-- TABLE: workflow_agent_runs
-- Purpose: track NemoClaw and AI agent invocation logs
CREATE TABLE IF NOT EXISTS workflow_agent_runs (
  run_id            UUID          PRIMARY KEY DEFAULT gen_random_uuid(),
  agent_name        TEXT          NOT NULL,  -- nemoclaw, import_mapper, enrichment_engine
  trigger_type      TEXT          NOT NULL,  -- import, enrichment, qd_update, writeback
  trigger_ref       TEXT,
  input_payload     JSONB         NOT NULL DEFAULT '{}'::jsonb,
  output_payload    JSONB         NOT NULL DEFAULT '{}'::jsonb,
  status            TEXT          NOT NULL DEFAULT 'running',  -- running, completed, failed
  duration_ms       INTEGER,
  error_detail      TEXT,
  started_at        TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
  completed_at      TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_wf_agent_runs_agent ON workflow_agent_runs (agent_name, started_at DESC);
CREATE INDEX IF NOT EXISTS idx_wf_agent_runs_status ON workflow_agent_runs (status);

-- ─────────────────────────────────────────────────────────────────────────────
-- TENANT HARDENING FOR SHARED CRM SURFACES
-- ─────────────────────────────────────────────────────────────────────────────

ALTER TABLE crm_people ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE crm_accounts ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE crm_leads ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE crm_opportunities ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE crm_property_interests ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE intel_interactions ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE intel_reminders ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE intel_qd_scores ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE intel_qd_timeseries ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE workflow_actions ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE workflow_approvals ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';
ALTER TABLE workflow_import_batches ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT 'tenant_velocity';

CREATE INDEX IF NOT EXISTS idx_crm_people_tenant_created ON crm_people (tenant_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_crm_leads_tenant_status ON crm_leads (tenant_id, status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_crm_opportunities_tenant_stage ON crm_opportunities (tenant_id, stage, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_crm_property_interests_tenant_person ON crm_property_interests (tenant_id, person_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_interactions_tenant_person ON intel_interactions (tenant_id, person_id, happened_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_reminders_tenant_status ON intel_reminders (tenant_id, status, due_at);
CREATE INDEX IF NOT EXISTS idx_intel_qd_scores_tenant_person ON intel_qd_scores (tenant_id, person_id, score_type);
CREATE INDEX IF NOT EXISTS idx_intel_qd_timeseries_tenant_person ON intel_qd_timeseries (tenant_id, person_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_wf_actions_tenant_status ON workflow_actions (tenant_id, status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_wf_import_batches_tenant_lifecycle ON workflow_import_batches (tenant_id, lifecycle, created_at DESC);

-- ─────────────────────────────────────────────────────────────────────────────
-- TRIGGERS: auto-update updated_at
-- ─────────────────────────────────────────────────────────────────────────────

CREATE OR REPLACE FUNCTION set_canonical_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$;

DO $$ DECLARE
  t TEXT;
BEGIN
  FOREACH t IN ARRAY ARRAY[
    'crm_people', 'crm_accounts', 'crm_leads', 'crm_opportunities',
    'inventory_projects', 'inventory_units',
    'workflow_actions', 'workflow_import_batches'
  ] LOOP
    EXECUTE format(
      'DROP TRIGGER IF EXISTS trg_%s_updated_at ON %s;
       CREATE TRIGGER trg_%s_updated_at
         BEFORE UPDATE ON %s
         FOR EACH ROW EXECUTE FUNCTION set_canonical_updated_at();',
      t, t, t, t
    );
  END LOOP;
END $$;

-- ─────────────────────────────────────────────────────────────────────────────
-- INVENTORY SEED: 14 Canonical Kolkata Projects
-- ─────────────────────────────────────────────────────────────────────────────

INSERT INTO inventory_projects (project_id, project_name, developer_name, city, micro_market)
VALUES
  (gen_random_uuid(), 'Eden Devprayag',          'Eden Group',          'Kolkata', 'Rajarhat'),
  (gen_random_uuid(), 'Sugam Prakriti',           'Sugam Homes',         'Kolkata', 'Barasat'),
  (gen_random_uuid(), 'Atri Aqua',                'Atri Developers',     'Kolkata', 'New Town'),
  (gen_random_uuid(), 'Atri Surya Toron',         'Atri Developers',     'Kolkata', 'Rajarhat'),
  (gen_random_uuid(), 'Siddha Suburbia Bungalow', 'Siddha Group',        'Kolkata', 'Madanpur'),
  (gen_random_uuid(), 'Merlin Avana',             'Merlin Group',        'Kolkata', 'Tangra'),
  (gen_random_uuid(), 'DTC Good Earth',           'DTC Projects',        'Kolkata', 'New Town'),
  (gen_random_uuid(), 'Siddha Serena',            'Siddha Group',        'Kolkata', 'New Town'),
  (gen_random_uuid(), 'Siddha Sky Waterfront',    'Siddha Group',        'Kolkata', 'Beliaghata'),
  (gen_random_uuid(), 'Godrej Blue',              'Godrej Properties',   'Kolkata', 'New Town'),
  (gen_random_uuid(), 'DTC Sojon',                'DTC Projects',        'Kolkata', 'Rajarhat'),
  (gen_random_uuid(), 'Shriram Grand City',       'Shriram Properties',  'Kolkata', 'Howrah'),
  (gen_random_uuid(), 'Godrej Elevate',           'Godrej Properties',   'Kolkata', 'Dum Dum'),
  (gen_random_uuid(), 'Ambuja Utpaala',           'Ambuja Neotia',       'Kolkata', 'Tollygunge')
ON CONFLICT (project_name) DO NOTHING;

-- ─────────────────────────────────────────────────────────────────────────────
-- COMMENTS
-- ─────────────────────────────────────────────────────────────────────────────

COMMENT ON TABLE crm_people IS 'Canonical person-level contact identity. Primary join key across all CRM tables.';
COMMENT ON TABLE crm_leads IS 'Funnel-stage commercial qualification. One person may have multiple lead contexts.';
COMMENT ON TABLE crm_opportunities IS 'Deal pipeline objects linked to leads and inventory.';
COMMENT ON TABLE intel_interactions IS 'Umbrella interaction event. All channels (WhatsApp, call, email, visit) link here.';
COMMENT ON TABLE intel_transcripts IS 'Speaker-segmented call transcripts. speaker_segments_json is first-class data.';
COMMENT ON TABLE intel_qd_scores IS 'Latest QD summary by score_type per client. UNIQUE constraint enforces one row per type.';
COMMENT ON TABLE inventory_projects IS 'Master project catalog. 14 canonical Kolkata projects seeded.';
COMMENT ON TABLE workflow_import_batches IS 'RawImportBatch contract. Immutable after upload.';
COMMENT ON TABLE workflow_writebacks IS 'AI-proposed canonical mutations. Never auto-execute without approval.';

-- -----------------------------------------------------------------------------
-- Synthetic CRM v2 enrichment columns and Oracle read models
-- -----------------------------------------------------------------------------

ALTER TABLE crm_people
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS engagement_score FLOAT,
  ADD COLUMN IF NOT EXISTS communication_preference TEXT,
  ADD COLUMN IF NOT EXISTS best_contact_time TEXT;

ALTER TABLE crm_households
  ADD COLUMN IF NOT EXISTS size INTEGER,
  ADD COLUMN IF NOT EXISTS combined_budget_band TEXT,
  ADD COLUMN IF NOT EXISTS decision_maker_id UUID REFERENCES crm_people(person_id) ON DELETE SET NULL;

ALTER TABLE crm_leads
  ADD COLUMN IF NOT EXISTS stage TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS broker_team TEXT,
  ADD COLUMN IF NOT EXISTS engagement_score FLOAT,
  ADD COLUMN IF NOT EXISTS last_activity_at TIMESTAMPTZ;

ALTER TABLE crm_opportunities
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS deal_velocity TEXT,
  ADD COLUMN IF NOT EXISTS risk_factors JSONB NOT NULL DEFAULT '[]'::jsonb;

ALTER TABLE crm_property_interests
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS last_discussed_at TIMESTAMPTZ;

ALTER TABLE crm_stage_history
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS transition_duration_days INTEGER;

ALTER TABLE intel_interactions
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS broker_team TEXT,
  ADD COLUMN IF NOT EXISTS sentiment TEXT,
  ADD COLUMN IF NOT EXISTS sentiment_score FLOAT,
  ADD COLUMN IF NOT EXISTS intent_label TEXT,
  ADD COLUMN IF NOT EXISTS emotion_tags JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS client_engagement_level TEXT;

ALTER TABLE intel_calls
  ADD COLUMN IF NOT EXISTS objection_tags JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS outcome_summary TEXT,
  ADD COLUMN IF NOT EXISTS follow_up_actions JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS call_quality_score FLOAT;

ALTER TABLE intel_emails
  ADD COLUMN IF NOT EXISTS sentiment TEXT,
  ADD COLUMN IF NOT EXISTS intent_label TEXT,
  ADD COLUMN IF NOT EXISTS engagement_score FLOAT,
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS response_expected BOOLEAN;

ALTER TABLE intel_reminders
  ADD COLUMN IF NOT EXISTS interaction_id UUID REFERENCES intel_interactions(interaction_id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS context_snippet TEXT,
  ADD COLUMN IF NOT EXISTS completion_percentage INTEGER,
  ADD COLUMN IF NOT EXISTS overdue_days INTEGER,
  ADD COLUMN IF NOT EXISTS outcome_notes TEXT;

ALTER TABLE intel_transcripts
  ADD COLUMN IF NOT EXISTS call_outcome TEXT,
  ADD COLUMN IF NOT EXISTS follow_up_required BOOLEAN,
  ADD COLUMN IF NOT EXISTS emotion_tags JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS call_summary TEXT;

ALTER TABLE intel_visits
  ADD COLUMN IF NOT EXISTS outcome_type TEXT,
  ADD COLUMN IF NOT EXISTS visit_duration_minutes INTEGER,
  ADD COLUMN IF NOT EXISTS interest_signals JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS interest_score FLOAT,
  ADD COLUMN IF NOT EXISTS companion_type TEXT,
  ADD COLUMN IF NOT EXISTS companion_count INTEGER,
  ADD COLUMN IF NOT EXISTS objections_raised JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS follow_up_required BOOLEAN,
  ADD COLUMN IF NOT EXISTS next_steps TEXT,
  ADD COLUMN IF NOT EXISTS broker_notes TEXT,
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT;

ALTER TABLE intel_whatsapp_threads
  ADD COLUMN IF NOT EXISTS broker_id TEXT,
  ADD COLUMN IF NOT EXISTS broker_name TEXT,
  ADD COLUMN IF NOT EXISTS topic_category TEXT,
  ADD COLUMN IF NOT EXISTS sentiment_direction TEXT,
  ADD COLUMN IF NOT EXISTS resolution_status TEXT;

ALTER TABLE intel_qd_scores
  ADD COLUMN IF NOT EXISTS score_drivers JSONB NOT NULL DEFAULT '[]'::jsonb,
  ADD COLUMN IF NOT EXISTS trend_direction TEXT,
  ADD COLUMN IF NOT EXISTS explanation TEXT,
  ADD COLUMN IF NOT EXISTS confidence FLOAT;

ALTER TABLE intel_qd_timeseries
  ADD COLUMN IF NOT EXISTS broker_id TEXT;

CREATE TABLE IF NOT EXISTS intel_email_threads (
  thread_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  subject TEXT,
  first_email_at TIMESTAMPTZ,
  last_email_at TIMESTAMPTZ,
  email_count INTEGER DEFAULT 0,
  participants JSONB NOT NULL DEFAULT '[]'::jsonb,
  status TEXT,
  broker_id TEXT,
  metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE TABLE IF NOT EXISTS intel_call_objections (
  objection_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  call_id UUID REFERENCES intel_calls(call_id) ON DELETE CASCADE,
  objection_type TEXT,
  category TEXT,
  severity TEXT,
  status TEXT,
  client_quote TEXT,
  agent_response TEXT,
  resolution_strategy TEXT,
  extracted_at TIMESTAMPTZ,
  confidence_score FLOAT,
  metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE TABLE IF NOT EXISTS intel_extracted_facts (
  fact_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  interaction_id UUID REFERENCES intel_interactions(interaction_id) ON DELETE SET NULL,
  person_id UUID REFERENCES crm_people(person_id) ON DELETE CASCADE,
  fact_type TEXT NOT NULL,
  fact_value TEXT,
  confidence FLOAT,
  extracted_from TEXT,
  source_context TEXT,
  extracted_at TIMESTAMPTZ,
  metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE TABLE IF NOT EXISTS read_last_contacted (
  person_id UUID PRIMARY KEY REFERENCES crm_people(person_id) ON DELETE CASCADE,
  last_contact_at TIMESTAMPTZ,
  last_channel TEXT,
  last_interaction_type TEXT,
  days_since_contact INTEGER,
  interactions_last_7d INTEGER,
  interactions_last_30d INTEGER,
  interactions_last_90d INTEGER,
  total_interactions INTEGER,
  current_stage TEXT,
  broker_id TEXT,
  broker_name TEXT,
  computed_at TIMESTAMPTZ,
  metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE TABLE IF NOT EXISTS read_next_best_action (
  person_id UUID PRIMARY KEY REFERENCES crm_people(person_id) ON DELETE CASCADE,
  recommended_action TEXT,
  priority TEXT,
  rationale TEXT,
  suggested_channel TEXT,
  due_within_days INTEGER,
  broker_id TEXT,
  broker_name TEXT,
  opportunity_context TEXT,
  computed_at TIMESTAMPTZ,
  metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX IF NOT EXISTS idx_read_last_contacted_at ON read_last_contacted (last_contact_at DESC);
CREATE INDEX IF NOT EXISTS idx_read_next_best_priority ON read_next_best_action (priority);
CREATE INDEX IF NOT EXISTS idx_intel_facts_person ON intel_extracted_facts (person_id, extracted_at DESC);
CREATE INDEX IF NOT EXISTS idx_intel_objections_call ON intel_call_objections (call_id);
