export const scenarios = [
 {id:'schema',title:'01 · Схема CSV',why:'Перед отчётом убедитесь, что дата, сумма и идентификаторы получили ожидаемые типы. Исходная таблица загружена как VARCHAR, очищенная — с явной схемой.',task:'Сравните orders_raw и orders_clean: почему amount_minor сначала строка?',sql:`SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_name IN ('orders_raw','orders_clean')
ORDER BY table_name, ordinal_position`,chart:null},
 {id:'quality',title:'02 · Проверка качества',why:'Выгрузка может содержать повторные заказы, невозможную дату и ошибочную сумму. Сначала найдите проблемные строки, потом считайте деньги.',task:'Найдите дубликат O004, ошибку даты O021 и сумму O022. У неизвестного клиента 999 другая проблема: её покажет JOIN.',sql:`SELECT order_id, order_date, amount_minor,
 CASE WHEN order_id IS NULL OR trim(order_id) = '' THEN 'Нет ID'
      WHEN TRY_CAST(order_date AS DATE) IS NULL THEN 'Неверная дата'
      WHEN TRY_CAST(amount_minor AS BIGINT) IS NULL OR TRY_CAST(amount_minor AS BIGINT) < 0 THEN 'Неверная сумма'
      WHEN count(*) OVER (PARTITION BY order_id) > 1 THEN 'Повторный ID'
      ELSE 'OK' END AS issue
FROM orders_raw
QUALIFY issue <> 'OK'
ORDER BY issue, order_id`,chart:null},
 {id:'summary',title:'03 · Выручка по городам',why:'Нужна сводка оплаченных работ для руководителя: число заказов, выручка и средний чек. Отмены и незавершённые работы исключаются явно.',task:'Замените city на manager и сравните другой разрез. Суммы хранятся в копейках.',sql:`SELECT city, count(*)::INTEGER AS orders,
 sum(amount_minor)::BIGINT AS revenue_minor,
 round(avg(amount_minor) / 100, 2)::DOUBLE AS average_rub
FROM orders_clean WHERE status = 'paid'
GROUP BY city ORDER BY revenue_minor DESC, city`,chart:'revenue_minor'},
 {id:'join',title:'04 · Клиенты и потерянные связи',why:'LEFT JOIN сохраняет заказы без клиента в справочнике. Это помогает найти неполную выгрузку вместо незаметно заниженного отчёта.',task:'Добавьте WHERE c.client_id IS NULL и получите список потерянных связей.',sql:`SELECT o.order_id, o.client_id,
 coalesce(c.name, 'Клиент не найден') AS client,
 coalesce(c.segment, 'Неизвестно') AS segment,
 o.amount_minor, o.status
FROM orders_clean o
LEFT JOIN clients c ON o.client_id = c.client_id
ORDER BY o.order_id`,chart:null},
 {id:'window',title:'05 · Рейтинг и накопительный итог',why:'Оконные функции сохраняют строки отчёта и добавляют место менеджера и накопленную выручку. Подходит для KPI и динамики продаж.',task:'Замените DENSE_RANK на ROW_NUMBER и обсудите, как обрабатывать одинаковую выручку.',sql:`WITH totals AS (
 SELECT manager, sum(amount_minor)::BIGINT AS revenue_minor
 FROM orders_clean WHERE status='paid' GROUP BY manager
)
SELECT manager, revenue_minor,
 dense_rank() OVER (ORDER BY revenue_minor DESC)::INTEGER AS place,
 sum(revenue_minor) OVER (ORDER BY revenue_minor DESC, manager
 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)::BIGINT AS running_minor
FROM totals ORDER BY revenue_minor DESC, manager`,chart:'revenue_minor'},
 {id:'json',title:'06 · Вложенный JSON',why:'Ответ API содержит вложенный объект context. DuckDB читает JSON в структурированные типы, после чего обычный SQL строит отчёт по источникам обращений.',task:'Замените context.channel на event_type. Посмотрите поля событий запросом SELECT * FROM events.',sql:`SELECT context.channel AS channel,
 count(*)::INTEGER AS events,
 sum(context.value_minor)::BIGINT AS pipeline_minor
FROM events GROUP BY context.channel
ORDER BY pipeline_minor DESC, channel`,chart:'pipeline_minor'},
 {id:'parquet',title:'07 · CSV и Parquet: сверка',why:'Parquet хранит схему и столбцы. Здесь одна и та же очищенная выгрузка реально прочитана из Parquet, а SQL проверяет количество строк и контрольную сумму.',task:'Сравните показатели; совпадение сумм не доказывает равенство всех строк. Для строгой сверки используйте EXCEPT ALL в обе стороны.',sql:`SELECT 'CSV → очищенная таблица' AS source,
 count(*)::INTEGER AS rows, sum(amount_minor)::BIGINT AS total_minor
FROM orders_clean
UNION ALL
SELECT 'Parquet → таблица', count(*)::INTEGER, sum(amount_minor)::BIGINT
FROM parquet_orders`,chart:'total_minor'},
 {id:'params',title:'08 · Отчёт с параметрами',why:'Период и минимальная сумма передаются отдельно от SQL через prepared statement. Пользовательские значения не склеиваются с текстом запроса.',task:'Измените даты и минимальную сумму в копейках. Начало включается, конец исключается.',sql:`SELECT order_id, order_date::VARCHAR AS day, city, amount_minor
FROM orders_clean
WHERE status='paid' AND order_date >= CAST($1 AS DATE)
 AND order_date < CAST($2 AS DATE) AND amount_minor >= CAST($3 AS BIGINT)
ORDER BY order_date, order_id`,chart:'amount_minor',params:true},
 {id:'export',title:'09 · Готовый отчёт и экспорт',why:'Сводка по дням пригодится для файла заказчику, графика в админке или ежедневного задания на VPS. Скачайте JSON, CSV и SQL и воспроизведите результат локально.',task:'Измените группировку или период. JSON сохраняет большие целые строками; безопасный CSV защищает текстовые ячейки от формул.',sql:`SELECT order_date::VARCHAR AS day,
 count(*)::INTEGER AS orders,
 sum(amount_minor)::BIGINT AS revenue_minor
FROM orders_clean WHERE status='paid'
GROUP BY order_date ORDER BY order_date`,chart:'revenue_minor'}
];
export const defaultParams=['2026-10-01','2026-10-08','100000'];
