<?php
// syncPlots.php

// DEBUG: show PHP errors (remove or lower in production)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

header("Content-Type: application/json");
header("Access-Control-Allow-Origin", "*");
header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
header("Access-Control-Allow-Headers", "Content-Type");

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    echo json_encode(["status" => "ok", "message" => "OPTIONS"]);
    exit;
}

// Include config.php which creates $pdo (MySQL connection)
require_once __DIR__ . '/../../config.php';

$method = $_SERVER['REQUEST_METHOD'];

try {
    if ($method === 'GET') {
        // PULL: send plots to desktop
        pullPlots($pdo);
        exit;
    }

    if ($method === 'POST') {
        // PUSH: receive plots from desktop
        $raw  = file_get_contents('php://input');
        $data = json_decode($raw, true);

        if (!is_array($data)) {
            http_response_code(400);
            echo json_encode(["error" => "Invalid JSON payload"]);
            exit;
        }

        syncPlots($pdo, $data);
        exit;
    }

    http_response_code(405);
    echo json_encode([
        "error"  => "Method not allowed",
        "method" => $method
    ]);
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode([
        "error"   => "Server error",
        "details" => $e->getMessage()
    ]);
}

/**
 * GET: send all plots to desktop for sync
 * Must match PlotSyncDto on the desktop:
 *  Id, AreaId, Code, FullPrice, Status, BuyerName, Notes, ClientId, LastModified
 */
function pullPlots(PDO $pdo)
{
    $stmt = $pdo->query("
        SELECT
            Id,
            AreaId,
            Code,
            FullPrice,
            Status,
            BuyerName,
            Notes,
            ClientId,
            LastModified
        FROM Plots
        ORDER BY Id
    ");
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo json_encode($rows);
}

/**
 * POST: receive plots from desktop.
 *
 * Rules:
 *  - Use LastModified (ISO from desktop vs MySQL timestamp) for conflict resolution.
 *  - When updating, do NOT wipe ClientId on the server with null/empty values.
 *  - Server is source of truth for existing ClientId; desktop can provide for new rows.
 */
function syncPlots(PDO $pdo, array $plots)
{
    $pdo->beginTransaction();

    // Get current row for a plot
    $select = $pdo->prepare("
        SELECT AreaId, Code, FullPrice, Status, BuyerName, Notes, ClientId, LastModified
        FROM Plots
        WHERE Id = :Id
    ");

    // Insert new plot
    $insert = $pdo->prepare("
        INSERT INTO Plots
        (Id, AreaId, Code, FullPrice, Status, BuyerName, Notes, ClientId, LastModified)
        VALUES
        (:Id, :AreaId, :Code, :FullPrice, :Status, :BuyerName, :Notes, :ClientId, :LastModified)
    ");

    // Update existing plot (conflict‑aware)
    $update = $pdo->prepare("
        UPDATE Plots
        SET AreaId      = :AreaId,
            Code        = :Code,
            FullPrice   = :FullPrice,
            Status      = :Status,
            BuyerName   = :BuyerName,
            Notes       = :Notes,
            ClientId    = :ClientId,
            LastModified= :LastModified
        WHERE Id = :Id
    ");

    $count = 0;

    foreach ($plots as $p) {
        $id = isset($p['Id']) ? (int)$p['Id'] : null;
        if ($id === null) {
            continue;
        }

        $areaId    = isset($p['AreaId']) ? (int)$p['AreaId'] : 0;
        $code      = $p['Code'] ?? '';
        $price     = isset($p['FullPrice']) && $p['FullPrice'] !== ''
            ? (float)$p['FullPrice']
            : 0.0;
        $statusRaw = $p['Status'] ?? null; // "0","1","2","Sold","Pending","Available"
        $buyerName = $p['BuyerName'] ?? null;
        $notes     = $p['Notes'] ?? null;
        $clientId  = isset($p['ClientId']) ? (int)$p['ClientId'] : null;

        // Normalize incoming LastModified (ISO from desktop) → MySQL datetime
        $incomingLast = $p['LastModified'] ?? null;
        if ($incomingLast === null) {
            $incomingLast = gmdate('Y-m-d\TH:i:s\Z');
        }
        $incomingTime = strtotime($incomingLast);
        if ($incomingTime === false) {
            $incomingTime = time();
        }
        $incomingLastMysql = gmdate('Y-m-d H:i:s', $incomingTime);

        // Try load current row
        $select->execute([':Id' => $id]);
        $row = $select->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            // New plot → INSERT
            $statusNumeric = normalize_plot_status($statusRaw);
            $insert->execute([
                ':Id'           => $id,
                ':AreaId'       => $areaId,
                ':Code'         => $code,
                ':FullPrice'    => $price,
                ':Status'       => $statusNumeric,
                ':BuyerName'    => $buyerName,
                ':Notes'        => $notes,
                ':ClientId'     => $clientId,
                ':LastModified' => $incomingLastMysql,
            ]);
            $count++;
            continue;
        }

        // Existing row → conflict check by LastModified
        $serverLast = $row['LastModified'];
        if (!empty($serverLast)) {
            $serverTime = strtotime($serverLast);
            if ($serverTime !== false && $incomingTime <= $serverTime) {
                // Server is newer or equal → skip
                continue;
            }
        }

        // Conflict‑safe merge.

        // 1) Status: merge numeric codes, stronger wins (Sold > Pending > Available).
        $serverStatus   = $row['Status'];
        $newStatus      = merge_plot_status($serverStatus, $statusRaw);

        // 2) ClientId:
        //    - if server has a non‑null ClientId, keep it (server = source of truth).
        //    - if server has null and desktop sends one, accept desktop value.
        $serverClientId = $row['ClientId'] !== null ? (int)$row['ClientId'] : null;
        if ($serverClientId !== null) {
            $clientIdToUse = $serverClientId;
        } else {
            $clientIdToUse = $clientId;
        }

        // 3) BuyerName/Notes: if desktop sends non‑empty, use it; otherwise keep server value.
        $serverBuyer = $row['BuyerName'];
        $serverNotes = $row['Notes'];

        $buyerToUse = (!empty($buyerName) ? $buyerName : $serverBuyer);
        $notesToUse = (!empty($notes) ? $notes : $serverNotes);

        $update->execute([
            ':Id'           => $id,
            ':AreaId'       => $areaId,
            ':Code'         => $code,
            ':FullPrice'    => $price,
            ':Status'       => $newStatus,
            ':BuyerName'    => $buyerToUse,
            ':Notes'        => $notesToUse,
            ':ClientId'     => $clientIdToUse,
            ':LastModified' => $incomingLastMysql,
        ]);

        $count++;
    }

    $pdo->commit();

    echo json_encode([
        "status" => "ok",
        "entity" => "plots",
        "synced" => $count
    ]);
}

/**
 * Normalize incoming status from desktop to a numeric code.
 * 0 = Available, 1 = Sold, 2 = Pending.
 */
function normalize_plot_status($status): int
{
    if ($status === null || $status === '') {
        return 0; // Available
    }

    $s = trim((string)$status);

    // numeric codes from desktop
    if (ctype_digit($s)) {
        $n = (int)$s;
        if (in_array($n, [0, 1, 2], true)) {
            return $n;
        }
        return 0;
    }

    // textual values
    if (strcasecmp($s, 'sold') === 0) {
        return 1;
    }
    if (strcasecmp($s, 'pending') === 0 || strcasecmp($s, 'reserved') === 0) {
        return 2;
    }

    // treat any other text as Available
    return 0;
}

/**
 * Merge server and desktop status such that a stronger state wins.
 * Strength: Sold (1) > Pending (2) > Available (0).
 */
function merge_plot_status($serverStatus, $incomingStatus): int
{
    $serverNorm   = normalize_plot_status($serverStatus);
    $incomingNorm = normalize_plot_status($incomingStatus);

    // ranking by business meaning:
    // 0 = Available, 2 = Pending, 1 = Sold
    $rank = [
        0 => 0, // Available
        2 => 1, // Pending
        1 => 2, // Sold
    ];

    $serverRank   = $rank[$serverNorm]   ?? 0;
    $incomingRank = $rank[$incomingNorm] ?? 0;

    // Prefer the stronger state
    if ($incomingRank > $serverRank) {
        return $incomingNorm;
    }

    return $serverNorm;
}
