<?php
/**
 * Strategy save/edit/archive — TRADERALIGN STRATEGY BUSINESS RULES redesign
 * (2026-08-11). Source of truth: "TRADERALIGN STRATEGY BUSINESS RULES.docx".
 *
 * Core rule this endpoint now enforces (previously it did not — every save
 * cloned a new strategy_versions row and repointed current_version_id, so a
 * trader editing a strategy 200 times produced 200 rows): editing an
 * existing, real (non-validation) strategy UPDATES its one strategy_versions
 * row IN PLACE — the row's own id never changes, no new row is ever
 * inserted for an edit, and a diff of what changed is written to
 * strategy_change_events/strategy_change_fields (src/StrategyChangeHistory.php)
 * instead. A brand-new strategy still gets its first strategy_versions row
 * inserted, exactly as before — that's strategy CREATION, not an edit.
 *
 * Validation strategies (strategies.is_validation = 1) are explicitly left
 * on the OLD behavior (upsert-by-given-id, whatever the caller sends) with
 * no change-event tracking — per explicit instruction, that domain is a
 * separate, not-yet-redesigned concept and must not be forced into this
 * model. In practice the redesigned Strategy Builder UI (public/strategy.php)
 * never loads a validation strategy anymore (see its own query filter), so
 * this branch only matters as a defensive fallback for a stale client.
 */
require_once __DIR__ . '/../../config/config.php';
require_once __DIR__ . '/../../src/ScopedDb.php';
require_once __DIR__ . '/../../src/Auth.php';
require_once __DIR__ . '/../../src/StrategyChangeHistory.php';

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

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

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

try {
    $dsn = "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=utf8mb4";
    $pdo = new PDO($dsn, DB_USER, DB_PASS, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit;
}

$auth = new Auth($pdo);
$userId = $auth->requireAuth();
$db = new ScopedDb($pdo, $userId);

$input = json_decode(file_get_contents('php://input'), true);

if (!$input) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid JSON']);
    exit;
}

// ── Archive (§34 — prefer archive over destroy) ────────────────────────
// Was previously unhandled by this endpoint entirely (a real, pre-existing
// bug — the button existed in the UI, but every archive attempt 400'd on
// "Missing strategy or version ID" since only strategy_id was ever sent).
if (($input['action'] ?? '') === 'archive') {
    $strategyId = $input['strategy_id'] ?? null;
    if (!$strategyId) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing strategy_id']);
        exit;
    }
    $ok = $db->update('strategies', $strategyId, ['is_active' => 0]);
    if (!$ok) {
        http_response_code(404);
        echo json_encode(['error' => 'Strategy not found']);
        exit;
    }
    echo json_encode(['success' => true]);
    exit;
}

$strategy = $input['strategy'] ?? [];
$version = $input['strategy_version'] ?? [];
$items = $input['checklist_items'] ?? [];

if (empty($strategy['id']) || empty($version['id'])) {
    http_response_code(400);
    echo json_encode(['error' => 'Missing strategy or version ID']);
    exit;
}

$pdo->beginTransaction();
try {
    $stmt = $pdo->prepare('SELECT id, trading_style, current_version_id, is_validation FROM strategies WHERE id = :id AND user_id = :uid');
    $stmt->execute([':id' => $strategy['id'], ':uid' => $userId]);
    $existingStrategy = $stmt->fetch();

    $isValidation = $existingStrategy ? (bool)$existingStrategy['is_validation'] : false;
    $trackAsRealStrategyEdit = $existingStrategy && !$isValidation;

    $changesToRecord = [];
    $changeEventId = null;
    $versionIdUsed = $version['id'];

    if ($trackAsRealStrategyEdit) {
        // ── Real strategy, editing — update the ONE existing declaration
        // row in place. The target row is the strategy's own real
        // current_version_id, never whatever id the client happened to
        // send — this is what makes it impossible for this endpoint to be
        // tricked (stale client state, replayed request, etc.) into
        // spawning a second row for an existing strategy.
        $targetVersionId = $existingStrategy['current_version_id'];
        if (!$targetVersionId) {
            $pdo->rollBack();
            http_response_code(409);
            echo json_encode(['error' => 'Strategy has no current version to update']);
            exit;
        }
        $versionIdUsed = $targetVersionId;

        $oldVerStmt = $pdo->prepare('SELECT * FROM strategy_versions WHERE id = :id AND user_id = :uid');
        $oldVerStmt->execute([':id' => $targetVersionId, ':uid' => $userId]);
        $oldVersion = $oldVerStmt->fetch() ?: [];

        $oldItemsStmt = $pdo->prepare("SELECT category, COALESCE(NULLIF(polished_text, ''), raw_text) AS text FROM checklist_items WHERE strategy_version_id = :svid AND user_id = :uid");
        $oldItemsStmt->execute([':svid' => $targetVersionId, ':uid' => $userId]);
        $oldItems = $oldItemsStmt->fetchAll();

        $stmt = $pdo->prepare("UPDATE strategies SET name = :name, description = :desc, trading_style = :style, updated_at = NOW() WHERE id = :id AND user_id = :uid");
        $stmt->execute([
            ':name' => $strategy['name'], ':desc' => $strategy['description'] ?? '',
            ':style' => $strategy['trading_style'], ':id' => $strategy['id'], ':uid' => $userId,
        ]);

        $stmt = $pdo->prepare("UPDATE strategy_versions SET session_preference=:sp, instruments_traded=:it, timeframes_traded=:tf, max_risk_per_trade_pct=:mr, max_losing_streak=:mls, max_trades_per_day=:mtd, max_trades_per_week=:mtw, backtested_win_rate=:bwr, backtested_avg_rr=:bar, setup_criteria=:sc, entry_triggers=:et, sl_rules=:sl, tp_rules=:tp, partial_close_rules=:pcr, move_to_be_rule=:mbr, polished_by=:pb WHERE id=:id AND user_id=:uid");
        $stmt->execute([
            ':sp' => json_encode($version['session_preference'] ?? []),
            ':it' => json_encode($version['instruments_traded'] ?? []), ':tf' => json_encode($version['timeframes_traded'] ?? []),
            ':mr' => $version['max_risk_per_trade_pct'],
            ':mls' => $version['max_losing_streak'], ':mtd' => $version['max_trades_per_day'],
            ':mtw' => $version['max_trades_per_week'], ':bwr' => $version['backtested_win_rate'],
            ':bar' => $version['backtested_avg_rr'], ':sc' => json_encode($version['setup_criteria'] ?? []),
            ':et' => json_encode($version['entry_triggers'] ?? []), ':sl' => json_encode($version['sl_rules'] ?? []),
            ':tp' => json_encode($version['tp_rules'] ?? []), ':pcr' => json_encode($version['partial_close_rules'] ?? []),
            ':mbr' => $version['move_to_be_rule'] ?? '', ':pb' => $version['polished_by'],
            ':id' => $targetVersionId, ':uid' => $userId,
        ]);

        $stmt = $pdo->prepare('DELETE FROM checklist_items WHERE strategy_version_id = :svid AND user_id = :uid');
        $stmt->execute([':svid' => $targetVersionId, ':uid' => $userId]);
        foreach ($items as $i => $item) {
            $stmt = $pdo->prepare('INSERT INTO checklist_items (id, strategy_version_id, user_id, category, position, raw_text, polished_text, verification_hint, is_required) VALUES (:id, :svid, :uid, :cat, :pos, :raw, :pol, :vh, :req)');
            $stmt->execute([
                ':id' => $item['id'] ?? ScopedDb::uuid(), ':svid' => $targetVersionId, ':uid' => $userId,
                ':cat' => $item['category'], ':pos' => $i,
                ':raw' => $item['raw_text'], ':pol' => $item['polished_text'],
                ':vh' => $item['verification_hint'] ?? '', ':req' => !empty($item['is_required']) ? 1 : 0,
            ]);
        }

        $newItemsForDiff = array_map(
            fn($it) => ['category' => $it['category'], 'text' => $it['polished_text'] ?: $it['raw_text']],
            $items
        );
        $changesToRecord = StrategyChangeHistory::diff(
            ['trading_style' => $existingStrategy['trading_style']],
            ['trading_style' => $strategy['trading_style']],
            $oldVersion,
            $version,
            $oldItems,
            $newItemsForDiff
        );
        $changeEventId = StrategyChangeHistory::record($pdo, $strategy['id'], $userId, $changesToRecord);
    } else {
        // ── Brand-new strategy's first save, OR a validation strategy
        // (untouched legacy path — no version-collapsing, no change
        // tracking; preserves this endpoint's original behavior for that
        // domain exactly). ──
        if ($existingStrategy) {
            $stmt = $pdo->prepare("UPDATE strategies SET name = :name, description = :desc, trading_style = :style, updated_at = NOW() WHERE id = :id AND user_id = :uid");
            $stmt->execute([
                ':name' => $strategy['name'], ':desc' => $strategy['description'] ?? '',
                ':style' => $strategy['trading_style'], ':id' => $strategy['id'], ':uid' => $userId,
            ]);
        } else {
            $stmt = $pdo->prepare('INSERT INTO strategies (id, user_id, name, description, trading_style, is_active) VALUES (:id, :uid, :name, :desc, :style, 1)');
            $stmt->execute([
                ':id' => $strategy['id'], ':uid' => $userId,
                ':name' => $strategy['name'], ':desc' => $strategy['description'] ?? '',
                ':style' => $strategy['trading_style'],
            ]);
        }

        $stmt = $pdo->prepare('SELECT id FROM strategy_versions WHERE id = :id AND user_id = :uid');
        $stmt->execute([':id' => $version['id'], ':uid' => $userId]);
        if ($stmt->fetch()) {
            $stmt = $pdo->prepare("UPDATE strategy_versions SET version_number=:vn, session_preference=:sp, instruments_traded=:it, timeframes_traded=:tf, max_risk_per_trade_pct=:mr, max_losing_streak=:mls, max_trades_per_day=:mtd, max_trades_per_week=:mtw, backtested_win_rate=:bwr, backtested_avg_rr=:bar, setup_criteria=:sc, entry_triggers=:et, sl_rules=:sl, tp_rules=:tp, partial_close_rules=:pcr, move_to_be_rule=:mbr, polished_by=:pb WHERE id=:id AND user_id=:uid");
            $stmt->execute([
                ':vn' => $version['version_number'], ':sp' => json_encode($version['session_preference'] ?? []),
                ':it' => json_encode($version['instruments_traded'] ?? []), ':tf' => json_encode($version['timeframes_traded'] ?? []),
                ':mr' => $version['max_risk_per_trade_pct'],
                ':mls' => $version['max_losing_streak'], ':mtd' => $version['max_trades_per_day'],
                ':mtw' => $version['max_trades_per_week'], ':bwr' => $version['backtested_win_rate'],
                ':bar' => $version['backtested_avg_rr'], ':sc' => json_encode($version['setup_criteria'] ?? []),
                ':et' => json_encode($version['entry_triggers'] ?? []), ':sl' => json_encode($version['sl_rules'] ?? []),
                ':tp' => json_encode($version['tp_rules'] ?? []), ':pcr' => json_encode($version['partial_close_rules'] ?? []),
                ':mbr' => $version['move_to_be_rule'] ?? '', ':pb' => $version['polished_by'],
                ':id' => $version['id'], ':uid' => $userId,
            ]);
        } else {
            $stmt = $pdo->prepare('INSERT INTO strategy_versions (id, strategy_id, user_id, version_number, session_preference, instruments_traded, timeframes_traded, max_risk_per_trade_pct, max_losing_streak, max_trades_per_day, max_trades_per_week, backtested_win_rate, backtested_avg_rr, setup_criteria, entry_triggers, sl_rules, tp_rules, partial_close_rules, move_to_be_rule, polished_by) VALUES (:id,:sid,:uid,:vn,:sp,:it,:tf,:mr,:mls,:mtd,:mtw,:bwr,:bar,:sc,:et,:sl,:tp,:pcr,:mbr,:pb)');
            $stmt->execute([
                ':id' => $version['id'], ':sid' => $strategy['id'], ':uid' => $userId,
                ':vn' => $version['version_number'], ':sp' => json_encode($version['session_preference'] ?? []),
                ':it' => json_encode($version['instruments_traded'] ?? []), ':tf' => json_encode($version['timeframes_traded'] ?? []),
                ':mr' => $version['max_risk_per_trade_pct'],
                ':mls' => $version['max_losing_streak'], ':mtd' => $version['max_trades_per_day'],
                ':mtw' => $version['max_trades_per_week'], ':bwr' => $version['backtested_win_rate'],
                ':bar' => $version['backtested_avg_rr'], ':sc' => json_encode($version['setup_criteria'] ?? []),
                ':et' => json_encode($version['entry_triggers'] ?? []), ':sl' => json_encode($version['sl_rules'] ?? []),
                ':tp' => json_encode($version['tp_rules'] ?? []), ':pcr' => json_encode($version['partial_close_rules'] ?? []),
                ':mbr' => $version['move_to_be_rule'] ?? '', ':pb' => $version['polished_by'],
            ]);
        }

        $stmt = $pdo->prepare('UPDATE strategies SET current_version_id = :cvid WHERE id = :id AND user_id = :uid');
        $stmt->execute([':cvid' => $version['id'], ':id' => $strategy['id'], ':uid' => $userId]);

        $stmt = $pdo->prepare('DELETE FROM checklist_items WHERE strategy_version_id = :svid AND user_id = :uid');
        $stmt->execute([':svid' => $version['id'], ':uid' => $userId]);
        foreach ($items as $i => $item) {
            $stmt = $pdo->prepare('INSERT INTO checklist_items (id, strategy_version_id, user_id, category, position, raw_text, polished_text, verification_hint, is_required) VALUES (:id, :svid, :uid, :cat, :pos, :raw, :pol, :vh, :req)');
            $stmt->execute([
                ':id' => $item['id'] ?? ScopedDb::uuid(), ':svid' => $version['id'], ':uid' => $userId,
                ':cat' => $item['category'], ':pos' => $i,
                ':raw' => $item['raw_text'], ':pol' => $item['polished_text'],
                ':vh' => $item['verification_hint'] ?? '', ':req' => !empty($item['is_required']) ? 1 : 0,
            ]);
        }
    }

    $pdo->commit();
    echo json_encode([
        'success' => true,
        'version_id' => $versionIdUsed,
        'change_event_id' => $changeEventId,
        'changes_recorded' => count($changesToRecord),
    ]);
} catch (\Throwable $e) {
    $pdo->rollBack();
    error_log('strategy/save.php failed: ' . $e->getMessage());
    http_response_code(500);
    echo json_encode(['error' => 'Failed to save strategy. Please try again.']);
}
