<?php
// index.php - ServiceDirect Svix webhook receiver
// Place in public_html/servicedirect/index.php
// Requires config.php loader in same directory that loads .env into getenv()/$_ENV

// Load environment (config.php should populate getenv/$_ENV)
if (file_exists(__DIR__ . '/config.php')) {
    require_once __DIR__ . '/config.php';
}

// Helper to get env with fallback
function env($name, $fallback = null) {
    $v = getenv($name);
    if ($v === false) {
        if (isset($_ENV[$name])) return $_ENV[$name];
        if (isset($_SERVER[$name])) return $_SERVER[$name];
        return $fallback;
    }
    return $v;
}

// Configuration from .env
$SIGNING_SECRET = env('SIGNING_SECRET', '');
$DB_HOST = env('DB_HOST', 'localhost');
$DB_PORT = (int)env('DB_PORT', 3306);
$DB_NAME = env('DB_NAME', 'servicedirect');
$DB_USER = env('DB_USER', 'servicedirect');
$DB_PASS = env('DB_PASS', '');
$LOG_DIR = env('LOG_DIR', 'logs');
$EVENTS_DIR = env('EVENTS_DIR', 'events');
$TOLERANCE_SECONDS = (int)env('TOLERANCE_SECONDS', 300);

// Basic checks
if (empty($SIGNING_SECRET)) {
    http_response_code(500);
    echo "Server misconfiguration: SIGNING_SECRET not set.";
    exit;
}

// ensure folders
if (!is_dir($LOG_DIR)) mkdir($LOG_DIR, 0755, true);
if (!is_dir($EVENTS_DIR)) mkdir($EVENTS_DIR, 0755, true);

$LOG_FILE = rtrim($LOG_DIR, '/\\') . '/webhook.log';
function log_line($msg) {
    global $LOG_FILE;
    $time = date('Y-m-d H:i:s T');
    file_put_contents($LOG_FILE, "[$time] $msg\n", FILE_APPEND | LOCK_EX);
}

// read raw body
$rawBody = file_get_contents('php://input');

// fetch Svix headers (handle variations in server)
$svix_id = $_SERVER['HTTP_SVIX_ID'] ?? $_SERVER['HTTP_SVIX-ID'] ?? null;
$svix_ts = $_SERVER['HTTP_SVIX_TIMESTAMP'] ?? $_SERVER['HTTP_SVIX-TIMESTAMP'] ?? null;
$svix_sig_header = $_SERVER['HTTP_SVIX_SIGNATURE'] ?? $_SERVER['HTTP_SVIX-SIGNATURE'] ?? null;

// If not present, show the "missing svix headers" message (this is normal when you open in browser)
if (!$svix_id || !$svix_ts || !$svix_sig_header) {
    http_response_code(200);
    echo "Missing svix headers";
    exit;
}

// validate timestamp (replay protection)
if (!ctype_digit((string)$svix_ts) || abs(time() - (int)$svix_ts) > $TOLERANCE_SECONDS) {
    http_response_code(400);
    log_line("Timestamp outside tolerance: now=" . time() . " svix_ts=$svix_ts");
    echo "Timestamp outside tolerance";
    exit;
}

// parse signing secret: format expected "whsec_<base64>"
$parts = explode('_', $SIGNING_SECRET, 2);
if (count($parts) !== 2) {
    http_response_code(500);
    log_line("Malformed SIGNING_SECRET in env.");
    echo "Server misconfiguration";
    exit;
}
$secret_key = base64_decode($parts[1], true);
if ($secret_key === false) {
    http_response_code(500);
    log_line("Failed to base64-decode SIGNING_SECRET part.");
    echo "Server misconfiguration";
    exit;
}

// compute expected signature: svix_id . '.' . svix_ts . '.' . raw_body
$signed_content = $svix_id . '.' . $svix_ts . '.' . $rawBody;
$expected_sig = base64_encode(hash_hmac('sha256', $signed_content, $secret_key, true));

// header may contain multiple signatures e.g. "v1,<sig> v1,<sig2>"
$ok = false;
$sigs = preg_split('/\s+/', trim($svix_sig_header));
foreach ($sigs as $entry) {
    if (!$entry) continue;
    if (strpos($entry, ',') !== false) {
        list($ver, $sigval) = explode(',', $entry, 2);
    } else {
        $sigval = $entry;
    }
    if (function_exists('hash_equals')) {
        if (hash_equals($sigval, $expected_sig)) { $ok = true; break; }
    } else {
        if ($sigval === $expected_sig) { $ok = true; break; }
    }
}

if (!$ok) {
    http_response_code(400);
    log_line("Signature verification failed. Expected: $expected_sig Header: $svix_sig_header");
    echo "Signature mismatch";
    exit;
}

// Verified signature: parse JSON
$json = json_decode($rawBody, true);
if ($json === null) {
    http_response_code(400);
    log_line("Verified payload but invalid JSON. Raw len=" . strlen($rawBody));
    echo "Invalid JSON";
    exit;
}

// save raw event file for debugging
$filename = rtrim($EVENTS_DIR, '/\\') . '/' . date('Ymd_His') . '_' . uniqid() . '.json';
file_put_contents($filename, json_encode(['headers'=>[
    'svix_id'=>$svix_id,'svix_ts'=>$svix_ts,'svix_sig'=>$svix_sig_header
],'body'=>$json], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));

// attempt DB connection if credentials present
$dbh = null;
try {
    if (!empty($DB_HOST) && $DB_NAME && $DB_USER !== '') {
        $dsn = "mysql:host={$DB_HOST};port={$DB_PORT};dbname={$DB_NAME};charset=utf8mb4";
        $dbh = new PDO($dsn, $DB_USER, $DB_PASS, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]);
    }
} catch (Exception $e) {
    log_line("DB connect failed: " . $e->getMessage());
    $dbh = null;
}

// Route events
$event_type = $json['type'] ?? ($json['event_type'] ?? 'unknown');
log_line("Received event: $event_type");

// handle lead.ready
if (stripos($event_type, 'lead.ready') !== false) {
    $data = $json['data'] ?? [];
    $lead_id = $data['lead_id'] ?? null;
    $request_id = $data['request_id'] ?? null;
    $payout = $data['payout'] ?? null;
    $outcome = $data['affiliate_payout_outcome'] ?? null;
    $caller = $data['lead_caller_phone_number'] ?? null;
    $created_ts = $data['lead_creation_timestamp'] ?? null;
    $service_category = $data['service_category'] ?? null;

    log_line("lead.ready: lead_id={$lead_id} request_id={$request_id} payout={$payout} outcome={$outcome} caller={$caller} service={$service_category}");

    if ($dbh && $lead_id) {
        try {
            $sql = "INSERT INTO sd_leads (lead_id, lead_creation_timestamp, lead_caller_phone_number, caller_id_name, integration_type, service_category, zip_code, payout, affiliate_payout_outcome, raw_payload)
                    VALUES (:lead_id, :lead_creation_timestamp, :lead_caller_phone_number, :caller_id_name, :integration_type, :service_category, :zip_code, :payout, :affiliate_payout_outcome, :raw_payload)
                    ON DUPLICATE KEY UPDATE
                      lead_creation_timestamp=VALUES(lead_creation_timestamp),
                      lead_caller_phone_number=VALUES(lead_caller_phone_number),
                      caller_id_name=VALUES(caller_id_name),
                      integration_type=VALUES(integration_type),
                      service_category=VALUES(service_category),
                      zip_code=VALUES(zip_code),
                      payout=VALUES(payout),
                      affiliate_payout_outcome=VALUES(affiliate_payout_outcome),
                      raw_payload=VALUES(raw_payload),
                      updated_at = CURRENT_TIMESTAMP";
            $stmt = $dbh->prepare($sql);
            $stmt->execute([
                ':lead_id'=>$lead_id,
                ':lead_creation_timestamp'=> $created_ts ? date('Y-m-d H:i:s', strtotime($created_ts)) : null,
                ':lead_caller_phone_number'=>$caller,
                ':caller_id_name'=>$data['caller_id_name'] ?? null,
                ':integration_type'=>$data['integration_type'] ?? null,
                ':service_category'=>$service_category,
                ':zip_code'=>$data['zip_code'] ?? null,
                ':payout'=>$payout,
                ':affiliate_payout_outcome'=>$outcome,
                ':raw_payload'=>json_encode($data)
            ]);
            log_line("DB upsert sd_leads lead_id=$lead_id OK");
        } catch (Exception $e) {
            log_line("DB upsert error for lead.ready: " . $e->getMessage());
        }
    }

    http_response_code(200);
    echo "processed lead.ready";
    exit;
}

// handle lead.update
if (stripos($event_type, 'lead.update') !== false) {
    $data = $json['data'] ?? [];
    $lead_id = $data['lead_id'] ?? null;
    $tracking_id = $data['tracking_id'] ?? null;
    $updates = $data['updates'] ?? [];

    log_line("lead.update: lead_id={$lead_id} updates_count=" . count($updates));

    if ($dbh && $lead_id && is_array($updates)) {
        try {
            $ins = $dbh->prepare("INSERT INTO sd_lead_updates (lead_id, tracking_id, property, value, ts) VALUES (:lead_id, :tracking_id, :property, :value, :ts)");
            foreach ($updates as $u) {
                $prop = $u['property'] ?? null;
                $val = $u['value'] ?? null;
                $ts = $u['timestamp'] ?? null;
                $ins->execute([
                    ':lead_id'=>$lead_id,
                    ':tracking_id'=>$tracking_id,
                    ':property'=>$prop,
                    ':value'=>json_encode($val),
                    ':ts'=>$ts
                ]);
            }
            log_line("DB insert sd_lead_updates lead_id=$lead_id OK, count=" . count($updates));
        } catch (Exception $e) {
            log_line("DB insert error for lead.update: " . $e->getMessage());
        }
    }

    // apply payout/outcome/service_needed updates to sd_leads if present
    if ($dbh && $lead_id && is_array($updates)) {
        $toUpdate = [];
        foreach ($updates as $u) {
            if ($u['property'] === 'payout') $toUpdate['payout'] = $u['value'];
            if ($u['property'] === 'affiliate_payout_outcome') $toUpdate['affiliate_payout_outcome'] = $u['value'];
            if ($u['property'] === 'service_needed') $toUpdate['service_needed'] = $u['value'];
        }
        if ($toUpdate) {
            $sets = [];
            $params = [':lead_id'=>$lead_id];
            foreach ($toUpdate as $k=>$v) {
                $sets[] = "`$k` = :$k";
                $params[":$k"] = is_array($v) ? json_encode($v) : $v;
            }
            try {
                $sql = "UPDATE sd_leads SET " . implode(',', $sets) . " WHERE lead_id = :lead_id";
                $dbh->prepare($sql)->execute($params);
                log_line("DB update sd_leads lead_id=$lead_id set: " . implode(',', array_keys($toUpdate)));
            } catch (Exception $e) {
                log_line("DB update sd_leads error: " . $e->getMessage());
            }
        }
    }

    http_response_code(200);
    echo "processed lead.update";
    exit;
}

// Unhandled event type
log_line("Unhandled event type: $event_type");
http_response_code(200);
echo "unhandled event type";
exit;
