<?php

declare(strict_types=1);

$dialect = (string)getenv('POC_DIALECT');
if (!in_array($dialect, ['mysql', 'dm'], true)) {
    throw new RuntimeException('Choose mysql or dm cooking fixture');
}
$root = (string)getenv('POC_APP_ROOT');
$base = rtrim((string)getenv('POC_BASE_URL'), '/');
if ($base === '' || strpos($base, 'http://127.0.0.1:') !== 0) {
    throw new RuntimeException('Loopback HTTP test runtime required');
}
$boundaryContext = stream_context_create(['http' => ['timeout' => 5]]);
$boundaryRaw = @file_get_contents($base . '/__poc_boundary', false, $boundaryContext);
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
if (!is_array($boundary) || empty($boundary['external_stubs'])
    || empty($boundary['redis_isolated'])) {
    throw new RuntimeException('Cooking POC isolation boundary missing');
}
$settings = [];
foreach (['HOST', 'PORT', 'SCHEMA', 'USER', 'PASSWORD'] as $name) {
    $settings[$name] = (string)getenv('POC_' . $name);
}
if ($dialect === 'mysql' && $settings['HOST'] === '') {
    $local = $root . '/application/config/local/database.php';
    $file = is_file($local) ? $local : $root . '/application/database.php';
    $config = (static function (string $path): array { return require $path; })($file);
    $settings = ['HOST' => (string)$config['hostname'],
        'PORT' => (string)($config['hostport'] ?? 3306),
        'SCHEMA' => (string)$config['database'],
        'USER' => (string)$config['username'],
        'PASSWORD' => (string)$config['password']];
}
if ($settings['HOST'] === '' || $settings['SCHEMA'] === '' || $settings['USER'] === '') {
    throw new RuntimeException('Cooking fixture database config unavailable');
}
$dsn = $dialect === 'dm'
    ? "dm:host={$settings['HOST']};port={$settings['PORT']};schema={$settings['SCHEMA']}"
    : "mysql:host={$settings['HOST']};port={$settings['PORT']};dbname={$settings['SCHEMA']};charset=utf8mb4";
$db = new PDO($dsn, $settings['USER'], $settings['PASSWORD'],
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
$t = static function (string $name) use ($dialect): string {
    return $dialect === 'dm' ? 'YDY_' . strtoupper($name) : 'ydy_' . $name;
};
$first = $dialect === 'dm' ? 'FETCH FIRST 1 ROWS ONLY' : 'LIMIT 1';
$db->query('SELECT 1')->fetchColumn();
$day = '2001-01-02';
$dayCheck = $db->prepare('SELECT COUNT(*) FROM ' . $t('recipes') . ' WHERE MENU_DATE=?');
$dayCheck->execute([$day]);
if ((int)$dayCheck->fetchColumn() !== 0) {
    throw new RuntimeException('Fixed historical day is not empty; do not insert fixture');
}
foreach ([$t('dishes'), $t('dish'), $t('recipes')] as $table) {
    if ($dialect === 'dm') {
        $trigger = $db->prepare('SELECT COUNT(*) FROM USER_TRIGGERS WHERE TABLE_NAME=? AND STATUS=?');
        $trigger->execute([$table, 'ENABLED']);
        $fk = $db->prepare('SELECT COUNT(*) FROM USER_CONSTRAINTS'
            . ' WHERE TABLE_NAME=? AND CONSTRAINT_TYPE=? AND STATUS=?');
        $fk->execute([$table, 'R', 'ENABLED']);
    } else {
        $trigger = $db->prepare('SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS'
            . ' WHERE TRIGGER_SCHEMA=? AND EVENT_OBJECT_TABLE=?');
        $trigger->execute([$settings['SCHEMA'], $table]);
        $fk = $db->prepare('SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE'
            . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=? AND REFERENCED_TABLE_NAME IS NOT NULL');
        $fk->execute([$settings['SCHEMA'], $table]);
    }
    if ((int)$trigger->fetchColumn() !== 0 || (int)$fk->fetchColumn() !== 0) {
        throw new RuntimeException('Cooking fixture table now has enabled trigger/FK');
    }
}
$restaurant = $db->query('SELECT ID FROM ' . $t('restaurants')
    . ' WHERE IS_DELETE=2 AND STATUS=1 ORDER BY ID ' . $first)->fetchColumn();
$composition = $db->query('SELECT ID FROM ' . $t('foods_composition')
    . ' ORDER BY ID ' . $first)->fetchColumn();
if ((int)$restaurant <= 0 || (int)$composition <= 0) {
    throw new RuntimeException('Existing read-only restaurant/composition reference missing');
}
$marker = 'CDXPARCOOK' . strtoupper(bin2hex(random_bytes(6)));
$uuids = ['dishes_a' => $marker . 'A', 'dishes_b' => $marker . 'B',
    'dish_a' => $marker . 'CA', 'dish_b' => $marker . 'CB',
    'recipe_a' => $marker . 'RA', 'recipe_b' => $marker . 'RB'];
$allUuids = array_values($uuids);
$makeIn = static function (array $values): string {
    return implode(',', array_fill(0, count($values), '?'));
};
foreach ([$t('dishes'), $t('dish'), $t('recipes')] as $table) {
    $check = $db->prepare('SELECT COUNT(*) FROM ' . $table
        . ' WHERE UUID IN (' . $makeIn($allUuids) . ')');
    $check->execute($allUuids);
    if ((int)$check->fetchColumn() !== 0) {
        throw new RuntimeException('Cooking fixture UUID collision');
    }
}
$send = static function (string $route, array $form) use ($base): array {
    $context = stream_context_create(['http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'timeout' => 20, 'ignore_errors' => true,
    ]]);
    $body = file_get_contents($base . $route, false, $context);
    $headers = $http_response_header ?? [];
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($headers[0] ?? ''), $match);
    $json = is_string($body) ? json_decode($body, true) : null;
    return ['http_status' => isset($match[1]) ? (int)$match[1] : 0,
        'json' => is_array($json) ? $json : null];
};
$results = [];
$committed = false;
try {
    $db->beginTransaction();
    $insertDishes = $db->prepare('INSERT INTO ' . $t('dishes')
        . ' (UUID,NAME,CATEGORY,STATUS,IS_DEL,PRICE) VALUES (?,?,1,1,1,1)');
    $insertDish = $db->prepare('INSERT INTO ' . $t('dish')
        . ' (UUID,DISHES_UUID,COMPOSITION_ID,COMPOSITION_NAME,COOK_METHOD,COMPOSITION_RATIO)'
        . ' VALUES (?,?,?,?,?,100)');
    $insertRecipe = $db->prepare('INSERT INTO ' . $t('recipes')
        . ' (UUID,DISHES_UUID,MENU_DATE,CREATE_TIME,CREATE_BY,MEAL_TIMES,TYPE,RESTAURANT_ID)'
        . ' VALUES (?,?,?,?,?,1,1,?)');
    foreach ([['a', 1], ['b', 14]] as $spec) {
        [$letter, $cookMethod] = $spec;
        $dishUuid = $uuids['dishes_' . $letter];
        $insertDishes->execute([$dishUuid, $marker . '-' . strtoupper($letter)]);
        $insertDish->execute([$uuids['dish_' . $letter], $dishUuid,
            (int)$composition, $marker . '-ingredient-' . $letter, $cookMethod]);
        $insertRecipe->execute([$uuids['recipe_' . $letter], $dishUuid,
            $day, date('Y-m-d H:i:s'), $marker, (int)$restaurant]);
    }
    $db->commit();
    $committed = true;

    $source = $db->prepare('SELECT d.COOK_METHOD,COUNT(*) AS N FROM ' . $t('recipes') . ' r'
        . ' JOIN ' . $t('dishes') . ' b ON b.UUID=r.DISHES_UUID'
        . ' JOIN ' . $t('dish') . ' d ON d.DISHES_UUID=b.UUID'
        . ' WHERE r.MENU_DATE=? AND r.RESTAURANT_ID=? AND r.MEAL_TIMES=1'
        . ' AND r.UUID IN (?,?) AND b.STATUS=1 AND b.IS_DEL=1'
        . ' GROUP BY d.COOK_METHOD ORDER BY d.COOK_METHOD');
    $source->execute([$day, (int)$restaurant, $uuids['recipe_a'], $uuids['recipe_b']]);
    $sourceRows = $source->fetchAll(PDO::FETCH_ASSOC);
    if (count($sourceRows) !== 2 || (int)$sourceRows[0]['cook_method'] !== 1
        || (int)$sourceRows[0]['n'] !== 1 || (int)$sourceRows[1]['cook_method'] !== 14
        || (int)$sourceRows[1]['n'] !== 1) {
        throw new RuntimeException('Synthetic cooking source rows not exact');
    }
    $results['source'] = ['status' => 'tested-pass',
        'dishes' => 2, 'ingredients' => 2, 'recipes' => 2,
        'distinct_methods' => 2];

    $baseForm = ['restaurant_id' => (int)$restaurant,
        'menu_date' => $day, 'meal_times' => 1];
    $primary = $send('/p/restaurant/cook_method', $baseForm);
    $primaryData = (array)($primary['json']['data'] ?? []);
    $primaryMap = (array)($primaryData['map'] ?? []);
    $primaryTable = (array)($primaryData['table'] ?? []);
    $primaryPass = $primary['http_status'] === 200
        && (int)($primary['json']['code'] ?? -1) === 0
        && count($primaryMap) === 20
        && (int)($primaryMap[1] ?? 0) === 1
        && (int)($primaryMap[14] ?? 0) === 1
        && array_sum(array_map('intval', $primaryMap)) === 2
        && (int)($primaryTable['recommend_num'] ?? -1) === 1
        && (int)($primaryTable['no_recommend_num'] ?? -1) === 1;
    $results['cook_method'] = ['status' => $primaryPass ? 'tested-pass' : 'tested-fail',
        'http_status' => $primary['http_status'],
        'business_code' => $primary['json']['code'] ?? null,
        'source_method_counts_match' => $primaryPass];

    $stageForm = ['restaurant_id' => (int)$restaurant,
        'start_time' => $day, 'end_time' => $day,
        'meal_times' => 1, 'cook' => 1];
    $recommended = $send('/p/restaurant/stage_cook_method', $stageForm);
    $recommendedData = (array)($recommended['json']['data'] ?? []);
    $recommendedMap = (array)($recommendedData['map'] ?? []);
    $recommendedTotals = array_map(static function ($part): int {
        return array_sum(array_map('intval', (array)$part));
    }, $recommendedMap);
    $recommendedPass = $recommended['http_status'] === 200
        && (int)($recommended['json']['code'] ?? -1) === 0
        && count($recommendedMap) === 14
        && (int)($recommendedTotals[1] ?? 0) === 1
        && array_sum($recommendedTotals) === 1
        && ($recommendedData['datex'][0] ?? null) === $day;
    $results['stage_recommended'] = ['status' => $recommendedPass ? 'tested-pass' : 'tested-fail',
        'http_status' => $recommended['http_status'],
        'business_code' => $recommended['json']['code'] ?? null,
        'source_method_count_matches' => $recommendedPass];

    $unrecommended = $send('/p/restaurant/stage_cook_method',
        array_replace($stageForm, ['cook' => 2]));
    $unrecommendedData = (array)($unrecommended['json']['data'] ?? []);
    $unrecommendedMap = (array)($unrecommendedData['map'] ?? []);
    $unrecommendedTotals = array_map(static function ($part): int {
        return array_sum(array_map('intval', (array)$part));
    }, $unrecommendedMap);
    $unrecommendedPass = $unrecommended['http_status'] === 200
        && (int)($unrecommended['json']['code'] ?? -1) === 0
        && count($unrecommendedMap) === 6
        && (int)($unrecommendedTotals[0] ?? 0) === 1
        && array_sum($unrecommendedTotals) === 1
        && ($unrecommendedData['datex'][0] ?? null) === $day;
    $results['stage_unrecommended'] = ['status' => $unrecommendedPass ? 'tested-pass' : 'tested-fail',
        'http_status' => $unrecommended['http_status'],
        'business_code' => $unrecommended['json']['code'] ?? null,
        'source_method_count_matches' => $unrecommendedPass];
} finally {
    if ($db->inTransaction()) $db->rollBack();
    foreach ([[$t('recipes'), $uuids['recipe_a'], $uuids['recipe_b']],
        [$t('dish'), $uuids['dish_a'], $uuids['dish_b']],
        [$t('dishes'), $uuids['dishes_a'], $uuids['dishes_b']]] as $target) {
        [$table, $first, $second] = $target;
        $delete = $db->prepare('DELETE FROM ' . $table . ' WHERE UUID IN (?,?)');
        $delete->execute([$first, $second]);
        $check = $db->prepare('SELECT COUNT(*) FROM ' . $table . ' WHERE UUID IN (?,?)');
        $check->execute([$first, $second]);
        if ((int)$check->fetchColumn() !== 0) {
            throw new RuntimeException('Synthetic cooking fixture not cleaned in ' . $table);
        }
    }
    $dayCheck->execute([$day]);
    if ((int)$dayCheck->fetchColumn() !== 0) {
        throw new RuntimeException('Historical cooking date not restored to zero recipes');
    }
}
$results['cleanup'] = ['status' => 'tested-pass',
    'dishes_rows' => 0, 'dish_rows' => 0, 'recipe_rows' => 0,
    'historical_day_rows' => 0];
echo json_encode(['dialect' => $dialect, 'module' => 'store-cooking-nonempty',
    'results' => $results], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
foreach ($results as $result) {
    if (($result['status'] ?? '') === 'tested-fail') exit(1);
}
