<?php
/**
 * controllers/ollamaGenerate.php
 *
 * AJAX POST handler: sends soil test data to a local Ollama instance and
 * returns an AI-generated agronomic interpretation for the requested section.
 *
 * Expected POST params:
 *   csrf_token  string  CSRF token
 *   rid         int     soil_records.id
 *   rand        string  soil_records.rand (ownership token)
 *   section     string  overview | ai_interpretation | foliar | microbial
 *
 * Requires Ollama running on http://localhost:11434
 * Default model: llama3.2 — change OLLAMA_MODEL below to suit your setup.
 */

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../lib/auth.php';
require_once __DIR__ . '/../lib/csrf.php';

header('Content-Type: application/json');

// ── Auth + CSRF ─────────────────────────────────────────────────────────────
if (!isLoggedIn()) {
    http_response_code(401);
    echo json_encode(['success' => false, 'error' => 'Not authenticated']);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['success' => false, 'error' => 'Method not allowed']);
    exit;
}

if (!verifyCsrfToken($_POST['csrf_token'] ?? '')) {
    http_response_code(403);
    echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
    exit;
}

// ── Input validation ────────────────────────────────────────────────────────
$recordId = (int)trim($_POST['rid']  ?? '');
$randId   = trim($_POST['rand']      ?? '');
$section  = trim($_POST['section']   ?? '');

$validSections = ['overview', 'ai_interpretation', 'foliar', 'microbial'];
if (!$recordId || $randId === '' || !in_array($section, $validSections, true)) {
    http_response_code(400);
    echo json_encode(['success' => false, 'error' => 'Invalid parameters']);
    exit;
}

// ── Load data ───────────────────────────────────────────────────────────────
try {
    $pdo    = getDBConnection();
    $userId = getCurrentUserId();

    $stmt = $pdo->prepare('SELECT * FROM soil_records WHERE id = ? AND rand = ?');
    $stmt->execute([$recordId, $randId]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$row) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Record not found']);
        exit;
    }

    $spec = [];
    if (!empty($row['soil_type'])) {
        $stmtSpec = $pdo->prepare('SELECT * FROM soil_specifications WHERE soil_type = ? LIMIT 1');
        $stmtSpec->execute([$row['soil_type']]);
        $spec = $stmtSpec->fetch(PDO::FETCH_ASSOC) ?: [];
    }

} catch (PDOException $e) {
    error_log('DB error in ollamaGenerate.php: ' . $e->getMessage());
    http_response_code(500);
    echo json_encode(['success' => false, 'error' => 'Database error']);
    exit;
}

// ── Build soil data summary for the prompt ──────────────────────────────────

/** Helper: format a numeric value safely, '' → 'N/A' */
function fv(mixed $v, int $dp = 2): string
{
    if ($v === null || $v === '') return 'N/A';
    return is_numeric($v) ? number_format((float)$v, $dp) : (string)$v;
}

/** Compare a value to a min/max range, return status string */
function rangeStatus(mixed $value, mixed $min, mixed $max): string
{
    if (!is_numeric($value)) return '';
    $v  = (float)$value;
    $lo = is_numeric($min) ? (float)$min : null;
    $hi = is_numeric($max) ? (float)$max : null;
    if ($lo !== null && $v < $lo) return 'DEFICIENT';
    if ($hi !== null && $v > $hi) return 'EXCESS';
    return 'IDEAL';
}

$r = $row;
$s = $spec;

$soilSummary = <<<TEXT
SOIL TEST RESULTS
=================
Client:       {$r['client_name']}
Location:     {$r['site_address']}, {$r['state_postcode']}
Crop:         {$r['sample_id']}
Soil Type:    {$r['soil_type']}
Lab Number:   {$r['lab_no']}
Date Sampled: {$r['date_sampled']}
Batch:        {$r['batch_no']}

PHYSICAL / GENERAL
------------------
pH (H2O):           {fv($r['ph_h2o'],   1)}  [target: 6.0–7.0]   {rangeStatus($r['ph_h2o'], 6.0, 7.0)}
pH (CaCl2):         {fv($r['ph_cacl2'], 1)}
EC (mS/cm):         {fv($r['ec'],       2)}
Organic Carbon (%): {fv($r['ocarbon'],  1)}
Organic Matter (%): {fv($r['omatter'],  1)}
CEC:                {fv($r['cec'],      2)}
TEC:                {fv($r['tec'],      2)}

MAJOR ELEMENTS (ppm)
---------------------
Calcium (Ca):       {fv($r['BS_ca_ppm'], 0)}  [min: {fv($s['ca_ppm_min'] ?? $r['ca_ppm_min'], 0)}, max: {fv($s['ca_ppm_max'] ?? $r['ca_ppm_max'], 0)}]  {rangeStatus($r['BS_ca_ppm'], $s['ca_ppm_min'] ?? $r['ca_ppm_min'] ?? null, $s['ca_ppm_max'] ?? $r['ca_ppm_max'] ?? null)}
Magnesium (Mg):     {fv($r['BS_mg_ppm'], 0)}  [min: {fv($s['mg_ppm_min'] ?? $r['mg_ppm_min'], 0)}, max: {fv($s['mg_ppm_max'] ?? $r['mg_ppm_max'], 0)}]  {rangeStatus($r['BS_mg_ppm'], $s['mg_ppm_min'] ?? $r['mg_ppm_min'] ?? null, $s['mg_ppm_max'] ?? $r['mg_ppm_max'] ?? null)}
Potassium (K):      {fv($r['BS_k_ppm'],  0)}  [min: {fv($s['k_ppm_min']  ?? $r['k_ppm_min'],  0)}, max: {fv($s['k_ppm_max']  ?? $r['k_ppm_max'],  0)}]  {rangeStatus($r['BS_k_ppm'],  $s['k_ppm_min']  ?? $r['k_ppm_min']  ?? null, $s['k_ppm_max']  ?? $r['k_ppm_max']  ?? null)}
Sodium (Na):        {fv($r['BS_na_ppm'], 0)}  [min: {fv($s['na_ppm_min'] ?? $r['na_ppm_min'], 0)}, max: {fv($s['na_ppm_max'] ?? $r['na_ppm_max'], 0)}]  {rangeStatus($r['BS_na_ppm'], $s['na_ppm_min'] ?? $r['na_ppm_min'] ?? null, $s['na_ppm_max'] ?? $r['na_ppm_max'] ?? null)}
Nitrate-N:          {fv($r['NO3_N'],    0)} ppm
Ammonium-N:         {fv($r['NH3_N'],    0)} ppm
Phosphate (P):      {fv($r['p_colwell'],0)} ppm  (Colwell)

TRACE ELEMENTS (ppm)
--------------------
Sulfur (S):         {fv($r['s_morgan'],  2)}
Boron (B):          {fv($r['b_cacl2'],   2)}
Manganese (Mn):     {fv($r['mn_dtpa'],   2)}
Copper (Cu):        {fv($r['cu_dtpa'],   2)}
Zinc (Zn):          {fv($r['zn_dtpa'],   2)}
Iron (Fe):          {fv($r['fe_dtpa'],   2)}
Aluminium (Al):     {fv($r['al'],        2)}
Silicon (Si):       {fv($r['sl_cacl2'],  2)}

BASE SATURATIONS (%)
--------------------
Calcium (Ca):       {fv($r['BS_ca2'],  2)}%  [min: {fv($s['cabs_min'] ?? null)}, max: {fv($s['cabs_max'] ?? null)}]  {rangeStatus($r['BS_ca2'], $s['cabs_min'] ?? null, $s['cabs_max'] ?? null)}
Magnesium (Mg):     {fv($r['BS_mg2'],  2)}%  [min: {fv($s['mgbs_min'] ?? null)}, max: {fv($s['mgbs_max'] ?? null)}]  {rangeStatus($r['BS_mg2'], $s['mgbs_min'] ?? null, $s['mgbs_max'] ?? null)}
Potassium (K):      {fv($r['BS_k'],    2)}%  [min: {fv($s['kbs_min']  ?? null)}, max: {fv($s['kbs_max']  ?? null)}]  {rangeStatus($r['BS_k'],   $s['kbs_min']  ?? null, $s['kbs_max']  ?? null)}
Sodium (Na):        {fv($r['BS_na'],   2)}%
Other Bases:        {fv($r['BS_ob'],   2)}%
Hydrogen:           {fv($r['BS_h'],    2)}%

RATIOS
------
Ca:Mg ratio:        {fv(is_numeric($r['ca_mehlick3']) && is_numeric($r['mg_mehlick3']) && (float)$r['mg_mehlick3'] != 0 ? (float)$r['ca_mehlick3'] / (float)$r['mg_mehlick3'] : null, 1)}  [recommended: {fv($s['ca_mg_ratio'] ?? null, 1)}]
C:N ratio:          {fv($r['c_n_ratio'], 1)}
TEXT;

// ── Section-specific prompts ────────────────────────────────────────────────
$prompts = [

    'overview' =>
        "You are an experienced Australian agronomist writing a professional soil analysis report.\n\n"
        . $soilSummary
        . "\n\nWrite a concise executive overview (3–4 paragraphs) suitable for a farmer. "
        . "Summarise the overall soil health, the most important deficiencies or imbalances, "
        . "and their likely impact on crop performance. Use plain language — avoid jargon. "
        . "Do not include specific product recommendations in this section.",

    'ai_interpretation' =>
        "You are an experienced Australian agronomist.\n\n"
        . $soilSummary
        . "\n\nProvide a detailed technical interpretation of these soil test results. "
        . "For each element that is DEFICIENT or in EXCESS, explain: "
        . "(1) the agronomic significance, "
        . "(2) the likely cause in Australian soils, "
        . "(3) the interactions with other nutrients shown in this test. "
        . "Also comment on the base saturation balance, pH implications, and organic matter. "
        . "Write in a professional tone suitable for an agronomist's report.",

    'foliar' =>
        "You are an experienced Australian agronomist.\n\n"
        . $soilSummary
        . "\n\nDesign a foliar spray program to correct the nutrient deficiencies shown in these soil test results. "
        . "List recommended applications by growth stage, including product types, rates (L/ha or kg/ha), "
        . "and timing. Focus on elements that are DEFICIENT. "
        . "Note any antagonisms to avoid (e.g. Ca and Mg competing). "
        . "Write as a practical program a farmer can follow.",

    'microbial' =>
        "You are an experienced Australian agronomist with expertise in soil biology.\n\n"
        . $soilSummary
        . "\n\nDesign a microbial and biological soil program suited to these test results. "
        . "Consider: the organic matter level, pH, and nutrient imbalances shown. "
        . "Recommend specific microbial inoculants, compost teas, or biological amendments "
        . "that would improve soil biology and nutrient cycling for the crop type listed. "
        . "Include timing and application rates where possible.",
];

// ── Call Ollama ─────────────────────────────────────────────────────────────
define('OLLAMA_URL',   'http://localhost:11434/api/generate');
define('OLLAMA_MODEL', 'llama3.2');   // change to match your installed model
define('OLLAMA_TIMEOUT', 120);        // seconds — LLM can be slow

$prompt  = $prompts[$section];
$payload = json_encode([
    'model'  => OLLAMA_MODEL,
    'prompt' => $prompt,
    'stream' => false,
]);

$ch = curl_init(OLLAMA_URL);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => OLLAMA_TIMEOUT,
    CURLOPT_CONNECTTIMEOUT => 5,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr  = curl_error($ch);
curl_close($ch);

if ($curlErr || $response === false) {
    http_response_code(502);
    echo json_encode([
        'success' => false,
        'error'   => 'Could not connect to Ollama: ' . ($curlErr ?: 'no response'),
    ]);
    exit;
}

if ($httpCode !== 200) {
    http_response_code(502);
    echo json_encode([
        'success' => false,
        'error'   => 'Ollama returned HTTP ' . $httpCode,
    ]);
    exit;
}

$ollamaData = json_decode($response, true);
$text = trim($ollamaData['response'] ?? '');

if ($text === '') {
    http_response_code(502);
    echo json_encode(['success' => false, 'error' => 'Ollama returned an empty response']);
    exit;
}

echo json_encode(['success' => true, 'text' => $text]);
exit;
