<?php

declare(strict_types=1);

$dialect = getenv('POC_DIALECT');
$baseUrl = rtrim((string)getenv('POC_BASE_URL'), '/');
if (!$baseUrl || !in_array($dialect, ['mysql', 'dm'], true)) exit(2);
$host = getenv('POC_HOST');
$port = getenv('POC_PORT');
$schema = getenv('POC_SCHEMA');
$dsn = $dialect === 'dm'
    ? "dm:host=$host;port=$port;schema=$schema"
    : "mysql:host=$host;port=$port;dbname=$schema;charset=utf8mb4";
$pdo = new PDO($dsn, getenv('POC_USER'), getenv('POC_PASSWORD'),
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$s = static function (string $name) use ($dialect): string {
    return $dialect === 'dm' ? 'YDY_' . strtoupper($name) : 'ydy_' . strtolower($name);
};
$candidate = $pdo->query('SELECT E.ID,E.CODE,E.RESTAURANT_ID FROM ' . $s('equipment') . ' E'
    . ' JOIN ' . $s('restaurants') . ' R ON E.RESTAURANT_ID=R.ID'
    . " WHERE E.DEL_FLAG=0 AND E.TYPE IN (2,3,4,10) AND E.CODE<>'' AND R.IS_DELETE=2"
    . ' ORDER BY E.ID ' . ($dialect === 'dm' ? 'FETCH FIRST 1 ROWS ONLY' : 'LIMIT 1'));
$equipment = array_change_key_case($candidate->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
$dishQuery = $pdo->query('SELECT UUID,MENU_QUANTITY,ATTRIBUTE FROM ' . $s('dishes')
    . ' WHERE IS_DEL=1 AND STATUS=1 AND ATTRIBUTE=1 ORDER BY ID '
    . ($dialect === 'dm' ? 'FETCH FIRST 1 ROWS ONLY' : 'LIMIT 1'));
$dish = array_change_key_case($dishQuery->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
if (!$equipment || !$dish || empty($dish['uuid'])) {
    throw new RuntimeException('Device recipe equipment/dish fixture missing');
}
$restaurantId = (int)$equipment['restaurant_id'];
$dishUuid = (string)$dish['uuid'];
$originalQuantity = (int)$dish['menu_quantity'];
$originalAttribute = (int)$dish['attribute'];
$sourceDate = '';
$targetDate = '';
$slotCount = $pdo->prepare('SELECT COUNT(*) FROM ' . $s('recipes')
    . ' WHERE RESTAURANT_ID=? AND MENU_DATE=? AND MEAL_TIMES=1');
for ($offset = 3650; $offset < 3670; $offset += 2) {
    $candidateSource = date('Y-m-d', strtotime('+' . $offset . ' day'));
    $candidateTarget = date('Y-m-d', strtotime('+' . ($offset + 1) . ' day'));
    $slotCount->execute([$restaurantId, $candidateSource]);
    $sourceCount = (int)$slotCount->fetchColumn();
    $slotCount->execute([$restaurantId, $candidateTarget]);
    if ($sourceCount === 0 && (int)$slotCount->fetchColumn() === 0) {
        $sourceDate = $candidateSource;
        $targetDate = $candidateTarget;
        break;
    }
}
if ($sourceDate === '') throw new RuntimeException('No empty future recipe slots available');
$post = static function (string $route, array $payload) use ($baseUrl): array {
    $context = stream_context_create(['http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($payload, '', '&', PHP_QUERY_RFC3986),
        'timeout' => 20, 'ignore_errors' => true,
    ]]);
    $body = file_get_contents($baseUrl . $route, false, $context);
    $response = is_string($body) ? json_decode($body, true) : null;
    if (!is_array($response)) throw new RuntimeException('Device recipe HTTP response not JSON');
    return $response;
};
$base = [
    'equipment_code' => (string)$equipment['code'],
    'restaurant_id' => $restaurantId,
    'dishes_uuid' => $dishUuid,
    'meal_times' => 1,
];
$recipeRows = $pdo->prepare('SELECT UUID FROM ' . $s('recipes')
    . ' WHERE RESTAURANT_ID=? AND MENU_DATE=? AND MEAL_TIMES=1 AND DISHES_UUID=?');
$dishCurrent = $pdo->prepare('SELECT MENU_QUANTITY,ATTRIBUTE FROM ' . $s('dishes') . ' WHERE UUID=?');
try {
    $add = $post('/api/device/recipe/addDishes', $base + ['menu_date' => $sourceDate]);
    $recipeRows->execute([$restaurantId, $sourceDate, $dishUuid]);
    $sourceRows = $recipeRows->fetchAll(PDO::FETCH_ASSOC);
    if ((int)($add['code'] ?? -1) !== 0
        || (int)($add['data']['added_count'] ?? 0) !== 1
        || count($sourceRows) !== 1) {
        throw new RuntimeException('Device recipe add/database readback mismatch');
    }
    $repeatAdd = $post('/api/device/recipe/addDishes', $base + ['menu_date' => $sourceDate]);
    $recipeRows->execute([$restaurantId, $sourceDate, $dishUuid]);
    if ((int)($repeatAdd['code'] ?? -1) !== 0
        || (int)($repeatAdd['data']['added_count'] ?? -1) !== 0
        || count($recipeRows->fetchAll(PDO::FETCH_ASSOC)) !== 1) {
        throw new RuntimeException('Device recipe duplicate add was not idempotent');
    }
    $copy = $post('/api/device/recipe/copy', [
        'equipment_code' => (string)$equipment['code'],
        'restaurant_id' => $restaurantId,
        'source_type' => 'day', 'source_date' => $sourceDate,
        'target_date' => $targetDate, 'meal_times' => 1,
        'overwrite' => 1, 'strict' => 1,
    ]);
    $recipeRows->execute([$restaurantId, $targetDate, $dishUuid]);
    if ((int)($copy['code'] ?? -1) !== 0
        || (int)($copy['data']['copied_count'] ?? 0) !== 1
        || count($recipeRows->fetchAll(PDO::FETCH_ASSOC)) !== 1) {
        throw new RuntimeException('Device recipe copy/database readback mismatch');
    }
    $dishCurrent->execute([$dishUuid]);
    $afterCopy = array_change_key_case($dishCurrent->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
    if ((int)($afterCopy['menu_quantity'] ?? -1) !== $originalQuantity + 2) {
        throw new RuntimeException('Dish menu quantity did not reflect add and copy');
    }
    foreach ([$targetDate, $sourceDate] as $date) {
        $deleted = $post('/api/device/recipe/deleteDishes', $base + ['menu_date' => $date]);
        $recipeRows->execute([$restaurantId, $date, $dishUuid]);
        if ((int)($deleted['code'] ?? -1) !== 0
            || (int)($deleted['data']['deleted_count'] ?? 0) !== 1
            || $recipeRows->fetch(PDO::FETCH_ASSOC)) {
            throw new RuntimeException('Device recipe delete/database readback mismatch');
        }
    }
    $dishCurrent->execute([$dishUuid]);
    $afterDelete = array_change_key_case($dishCurrent->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
    if ((int)($afterDelete['menu_quantity'] ?? -1) !== $originalQuantity
        || (int)($afterDelete['attribute'] ?? -1) !== $originalAttribute) {
        throw new RuntimeException('Dish counters or attribute were not restored');
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed',
        'recipe_add_duplicate_copy_delete' => true,
        'source_and_target_rows_after_cleanup' => 0,
        'dish_quantity_restored' => true]) . PHP_EOL;
} finally {
    $remaining = 0;
    foreach ([$sourceDate, $targetDate] as $date) {
        $recipeRows->execute([$restaurantId, $date, $dishUuid]);
        $rows = $recipeRows->fetchAll(PDO::FETCH_ASSOC);
        if (count($rows) > 1) throw new RuntimeException('Unexpected recipes in synthetic future slot');
        if ($rows) {
            $pdo->prepare('DELETE FROM ' . $s('recipes')
                . ' WHERE RESTAURANT_ID=? AND MENU_DATE=? AND MEAL_TIMES=1 AND DISHES_UUID=?')
                ->execute([$restaurantId, $date, $dishUuid]);
            $remaining += count($rows);
        }
    }
    $dishCurrent->execute([$dishUuid]);
    $observed = array_change_key_case($dishCurrent->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
    $currentQuantity = (int)($observed['menu_quantity'] ?? -1);
    if ($currentQuantity !== $originalQuantity || (int)($observed['attribute'] ?? -1) !== $originalAttribute) {
        if ($currentQuantity < $originalQuantity - 2 || $currentQuantity > $originalQuantity + 2) {
            throw new RuntimeException('Dish counter changed beyond synthetic recipe scope');
        }
        $restore = $pdo->prepare('UPDATE ' . $s('dishes')
            . ' SET MENU_QUANTITY=?,ATTRIBUTE=? WHERE UUID=? AND MENU_QUANTITY=?');
        $restore->execute([$originalQuantity, $originalAttribute, $dishUuid, $currentQuantity]);
        if ($restore->rowCount() !== 1) throw new RuntimeException('Dish state restore compare-and-set failed');
    }
    foreach ([$sourceDate, $targetDate] as $date) {
        $slotCount->execute([$restaurantId, $date]);
        if ((int)$slotCount->fetchColumn() !== 0) {
            throw new RuntimeException('Synthetic device recipe slot cleanup left rows');
        }
    }
}
