<?php
/**
 * MJ Marketplace v3.0.4.2.9 helpers.
 * Lead action popup, notification de-duplicate, and lightweight speed caches.
 */
if (!function_exists('mj429_table_exists')) {
function mj429_table_exists(string $table): bool {
    try { if (function_exists('table_exists')) { return table_exists($table); } $r = one('SELECT COUNT(*) c FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?', [$table]); return (int)($r['c'] ?? 0) > 0; }
    catch (Throwable $e) { return false; }
}
}
if (!function_exists('mj429_column_exists')) {
function mj429_column_exists(string $table, string $column): bool {
    try { if (function_exists('mj_column_exists')) { return mj_column_exists($table,$column); } $r = one('SELECT COUNT(*) c FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? AND COLUMN_NAME=?', [$table,$column]); return (int)($r['c'] ?? 0) > 0; }
    catch (Throwable $e) { return false; }
}
}
if (!function_exists('mj429_add_column')) {
function mj429_add_column(string $table, string $column, string $definition): void {
    try {
        if (mj429_table_exists($table) && !mj429_column_exists($table,$column)) {
            q('ALTER TABLE `'.preg_replace('/[^a-zA-Z0-9_]/','',$table).'` ADD COLUMN `'.preg_replace('/[^a-zA-Z0-9_]/','',$column).'` '.$definition);
        }
    } catch (Throwable $e) {}
}
}
if (!function_exists('mj429_add_index')) {
function mj429_add_index(string $table, string $index, string $cols, bool $unique=false): void {
    try {
        if (!mj429_table_exists($table)) { return; }
        $exists = one('SELECT COUNT(*) c FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? AND INDEX_NAME=?', [$table,$index]);
        if ((int)($exists['c'] ?? 0) <= 0) {
            q('ALTER TABLE `'.preg_replace('/[^a-zA-Z0-9_]/','',$table).'` ADD '.($unique?'UNIQUE ':'').'KEY `'.preg_replace('/[^a-zA-Z0-9_]/','',$index).'` ('.$cols.')');
        }
    } catch (Throwable $e) {}
}
}
if (!function_exists('mj429_ensure_schema')) {
function mj429_ensure_schema(): void {
    static $done=false; if($done) return; $done=true;
    if (!function_exists('q')) { return; }
    try { if (function_exists('mjv3041_ensure_light_schema')) { mjv3041_ensure_light_schema(); } } catch (Throwable $e) {}
    try {
        q("CREATE TABLE IF NOT EXISTS mj_notifications (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            user_id BIGINT NULL, admin_id BIGINT NULL, user_type VARCHAR(20) NULL,
            title VARCHAR(255) NOT NULL, message TEXT NULL, body TEXT NULL, type VARCHAR(80) NULL, priority VARCHAR(40) NOT NULL DEFAULT 'normal',
            link VARCHAR(500) NULL, action_url VARCHAR(500) NULL, url VARCHAR(500) NULL,
            notification_key VARCHAR(191) NULL, dedupe_hash CHAR(40) NULL, source_type VARCHAR(80) NULL, source_id BIGINT NULL, lead_id BIGINT NULL,
            is_read TINYINT(1) NOT NULL DEFAULT 0, is_popup_shown TINYINT(1) NOT NULL DEFAULT 0, popup_shown_at DATETIME NULL, read_at DATETIME NULL,
            is_archived TINYINT(1) NOT NULL DEFAULT 0, archived_at DATETIME NULL, meta_json LONGTEXT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            INDEX(user_id), INDEX(admin_id), INDEX(user_type), INDEX(is_read), INDEX(is_popup_shown), INDEX(created_at), INDEX(notification_key), INDEX(dedupe_hash)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    } catch (Throwable $e) {}
    foreach ([
        ['notification_key','VARCHAR(191) NULL'], ['dedupe_hash','CHAR(40) NULL'], ['source_type','VARCHAR(80) NULL'], ['source_id','BIGINT NULL'], ['lead_id','BIGINT NULL'], ['is_archived','TINYINT(1) NOT NULL DEFAULT 0'], ['archived_at','DATETIME NULL'], ['popup_shown_at','DATETIME NULL'], ['is_popup_shown','TINYINT(1) NOT NULL DEFAULT 0'], ['read_at','DATETIME NULL']
    ] as $c) { mj429_add_column('mj_notifications',$c[0],$c[1]); }
    mj429_add_column('seller_leads','updated_at','DATETIME NULL');
    mj429_add_column('seller_leads','last_activity_at','DATETIME NULL');
    mj429_add_column('seller_leads','followup_at','DATETIME NULL');
    mj429_add_column('seller_leads','is_read','TINYINT(1) NOT NULL DEFAULT 0');
    mj429_add_index('mj_notifications','idx_mj429_notify_recipient','user_type,user_id,admin_id,is_read,created_at');
    mj429_add_index('mj_notifications','idx_mj429_notify_key','notification_key');
    mj429_add_index('mj_notifications','idx_mj429_notify_source','source_type,source_id,lead_id');
    mj429_add_index('seller_leads','idx_mj429_leads_seller_status_date','seller_user_id,status,created_at');
    mj429_add_index('seller_leads','idx_mj429_leads_source','source_type,source_id');
    mj429_add_index('seller_lead_activities','idx_mj429_lead_activities','lead_id,created_at');
}
}
if (!function_exists('mj429_normalize_url_key')) {
function mj429_normalize_url_key(string $url): string {
    $url = trim($url); if ($url==='') return '';
    $url = preg_replace('/([?&])(success|saved|message|flash)=[^&]+/','', $url);
    return strtolower($url);
}
}
if (!function_exists('mj429_notification_key')) {
function mj429_notification_key(string $userType, ?int $userId, string $type, string $title, string $message='', string $url='', array $meta=[]): array {
    $leadId = (int)($meta['lead_id'] ?? $meta['lead'] ?? 0);
    $sourceType = (string)($meta['source_type'] ?? $meta['legacy_type'] ?? $type);
    $sourceId = (int)($meta['source_id'] ?? 0);
    $recipient = $userType . ':' . (int)($userId ?? 0);
    if ($leadId > 0) {
        $base = $recipient . ':lead:' . $leadId;
    } elseif ($sourceId > 0) {
        $base = $recipient . ':' . strtolower($sourceType ?: $type) . ':' . $sourceId;
    } else {
        $base = $recipient . ':' . strtolower($type) . ':' . sha1(strtolower(trim($title).'|'.trim($message).'|'.mj429_normalize_url_key($url)));
    }
    return [$base, sha1($base), $sourceType, $sourceId, $leadId];
}
}
if (!function_exists('mj429_notify')) {
function mj429_notify(string $userType, ?int $userId, string $title, string $message = '', string $type = 'info', string $url = '', array $meta = []): int {
    mj429_ensure_schema();
    if (!mj429_table_exists('mj_notifications')) { return 0; }
    [$nkey,$hash,$sourceType,$sourceId,$leadId] = mj429_notification_key($userType,$userId,$type,$title,$message,$url,$meta);
    try {
        $recipientWhere = ($userType==='admin') ? "(user_type='admin' OR admin_id IS NOT NULL)" : "(user_type='user' OR user_type IS NULL OR user_type='') AND user_id=".(int)$userId;
        $existing = one("SELECT id FROM mj_notifications WHERE notification_key=? AND is_archived=0 AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR) LIMIT 1", [$nkey]);
        if ($existing) { return (int)$existing['id']; }
        $existing = one("SELECT id FROM mj_notifications WHERE dedupe_hash=? AND is_archived=0 AND created_at >= DATE_SUB(NOW(), INTERVAL 20 MINUTE) LIMIT 1", [$hash]);
        if ($existing) { return (int)$existing['id']; }
    } catch (Throwable $e) {}
    $cols = [];
    try { $cols = function_exists('mjv3041_columns') ? mjv3041_columns('mj_notifications') : array_map(static function($r){ return (string)$r['COLUMN_NAME']; }, all_rows('SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME="mj_notifications"')); } catch(Throwable $e) { $cols=[]; }
    $data=[];
    $put = function($col,$val) use (&$data,$cols){ if(in_array($col,$cols,true)) $data[$col]=$val; };
    $put('user_type',$userType); $put('user_id',$userType==='user'?($userId?:null):null); $put('admin_id',$userType==='admin'?($userId?:null):null);
    $put('title',$title); $put('message',$message); $put('body',$message); $put('type',$type); $put('priority',in_array($type,['lead','chat','payment','review'],true)?'high':'normal');
    $put('link',$url); $put('action_url',$url); $put('url',$url); $put('is_read',0); $put('is_popup_shown',0); $put('notification_key',$nkey); $put('dedupe_hash',$hash); $put('source_type',$sourceType); $put('source_id',$sourceId?:null); $put('lead_id',$leadId?:null); $put('is_archived',0); $put('meta_json',$meta?json_encode($meta, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES):null); $put('created_at',date('Y-m-d H:i:s'));
    if (!$data) return 0;
    try { $names=array_keys($data); q('INSERT INTO mj_notifications (`'.implode('`,`',$names).'`) VALUES ('.implode(',', array_fill(0,count($names),'?')).')', array_values($data)); return (int)db()->lastInsertId(); }
    catch (Throwable $e) { return 0; }
}
}
if (!function_exists('mj429_dedupe_notification_rows')) {
function mj429_dedupe_notification_rows(array $rows): array {
    $seen=[]; $out=[]; $leadCount=0; $leadIds=[];
    foreach($rows as $r){
        $key = (string)($r['notification_key'] ?? '');
        if ($key==='') { $key = sha1(strtolower(trim((string)($r['title']??'')).'|'.trim((string)($r['message']??$r['body']??'')).'|'.trim((string)($r['link']??$r['action_url']??$r['url']??'')))); }
        if (isset($seen[$key])) { continue; }
        $seen[$key]=1; $r['_dedupe_key']=$key; $out[]=$r;
        if (strtolower((string)($r['type']??''))==='lead' || stripos((string)($r['title']??''),'lead')!==false || stripos((string)($r['title']??''),'enquiry')!==false) { $leadCount++; if(!empty($r['lead_id'])) $leadIds[]=(int)$r['lead_id']; }
    }
    if ($leadCount > 1) {
        $first=$out[0];
        array_unshift($out, ['id'=>0,'title'=>$leadCount.' new leads received','message'=>'Open your leads inbox to follow up.','type'=>'lead','link'=>'leads.php','action_url'=>'leads.php','is_read'=>0,'created_at'=>date('Y-m-d H:i:s'),'notification_key'=>'group_leads_'.sha1(json_encode($leadIds ?: array_column($out,'id'))),'_dedupe_key'=>'group_leads']);
    }
    return $out;
}
}
if (!function_exists('mj429_archive_duplicate_notifications')) {
function mj429_archive_duplicate_notifications(): int {
    mj429_ensure_schema();
    $affected=0;
    try {
        q("UPDATE mj_notifications n
           INNER JOIN mj_notifications keepn ON keepn.notification_key=n.notification_key AND keepn.id>n.id
           SET n.is_archived=1,n.is_read=1,n.archived_at=NOW()
           WHERE n.notification_key IS NOT NULL AND n.notification_key<>'' AND n.is_archived=0");
        $affected += (int)(function_exists('db') ? db()->query('SELECT ROW_COUNT()')->fetchColumn() : 0);
    } catch (Throwable $e) {}
    return $affected;
}
}
