-- Contact discovery from mail messages: pseudo-contacts, signatures, enrichment suggestions. CREATE TABLE contact_discovery_scans ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, status TEXT NOT NULL DEFAULT 'pending', messages_scanned INT NOT NULL DEFAULT 0, profiles_found INT NOT NULL DEFAULT 0, error_message TEXT, started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ ); CREATE INDEX idx_contact_discovery_scans_user ON contact_discovery_scans(user_id, started_at DESC); CREATE TABLE contact_discovered_profiles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, scan_id UUID REFERENCES contact_discovery_scans(id) ON DELETE SET NULL, person_group_id UUID, display_name TEXT NOT NULL DEFAULT '', primary_email TEXT NOT NULL, all_emails JSONB NOT NULL DEFAULT '[]', message_count INT NOT NULL DEFAULT 0, sent_count INT NOT NULL DEFAULT 0, received_count INT NOT NULL DEFAULT 0, spam_count INT NOT NULL DEFAULT 0, forwarded_count INT NOT NULL DEFAULT 0, is_mailing_list BOOLEAN NOT NULL DEFAULT false, is_disposable BOOLEAN NOT NULL DEFAULT false, is_spam_heavy BOOLEAN NOT NULL DEFAULT false, classification_reason TEXT, linked_contact_uid TEXT, enrichment_status TEXT NOT NULL DEFAULT 'pending', enriched_data JSONB, enriched_at TIMESTAMPTZ, status TEXT NOT NULL DEFAULT 'suggested', rejected_at TIMESTAMPTZ, accepted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_message_at TIMESTAMPTZ, UNIQUE(user_id, primary_email) ); CREATE INDEX idx_contact_discovered_profiles_user_status ON contact_discovered_profiles(user_id, status); CREATE INDEX idx_contact_discovered_profiles_user_email ON contact_discovered_profiles(user_id, lower(primary_email)); CREATE TABLE contact_discovered_signatures ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), profile_id UUID NOT NULL REFERENCES contact_discovered_profiles(id) ON DELETE CASCADE, message_id UUID REFERENCES messages(id) ON DELETE SET NULL, signature_text TEXT NOT NULL DEFAULT '', signature_html TEXT, message_date TIMESTAMPTZ NOT NULL, confidence REAL NOT NULL DEFAULT 0.5, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_contact_discovered_signatures_profile ON contact_discovered_signatures(profile_id, message_date DESC); CREATE TABLE contact_enrichment_suggestions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, profile_id UUID REFERENCES contact_discovered_profiles(id) ON DELETE CASCADE, target_contact_uid TEXT, suggestion_type TEXT NOT NULL, field_path TEXT NOT NULL, suggested_value TEXT NOT NULL, suggested_label TEXT NOT NULL DEFAULT '', confidence REAL NOT NULL DEFAULT 0.5, source_signature_id UUID REFERENCES contact_discovered_signatures(id) ON DELETE SET NULL, status TEXT NOT NULL DEFAULT 'pending', rejected_at TIMESTAMPTZ, accepted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(user_id, profile_id, field_path, suggested_value) ); CREATE INDEX idx_contact_enrichment_suggestions_user ON contact_enrichment_suggestions(user_id, status); CREATE TABLE contact_discovery_rejections ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, rejection_key TEXT NOT NULL, rejection_type TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(user_id, rejection_key, rejection_type) ); CREATE INDEX idx_contact_discovery_rejections_user ON contact_discovery_rejections(user_id);