"""
Скрипт демонстрации расширения датасета X5 всеми доступными способами.

Запуск:
    python src/ml/expand_dataset.py

Подходы:
    1. Data Augmentation (GaussianNoise + TimeWarping + Scaling + Mixup + WindowDropout)
    2. H1 таймфрейм (7,602 бара → ~5,000+ сэмплов)
    3. Multi-ticker pre-training (SBER, GAZP, LKOH, ... → 10,000+ сэмплов)
    4. Комбинированный (H1 + Augmentation + Multi-ticker fine-tune)
"""

import os
import sys
from pathlib import Path

import numpy as np

# Добавляем корень проекта
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

SAVED_DIR = PROJECT_ROOT / 'src' / 'ml' / 'models' / 'saved'


def approach_1_data_augmentation():
    """
    Подход 1: Data Augmentation на существующем датасете X5 D1.

    Расширяет 150 train сэмплов до 900 с помощью 5 методов.
    """
    print("\n" + "=" * 70)
    print("📊 ПОДХОД 1: DATA AUGMENTATION (X5 D1)")
    print("=" * 70)

    h5_path = SAVED_DIR / 'x5_d1_dataset.h5'
    if not h5_path.exists():
        print(f"  ⚠️ Датасет не найден: {h5_path}")
        print(f"  Сначала запустите: python src/ml/features/pipeline.py")
        return None

    import h5py
    with h5py.File(h5_path, 'r') as f:
        X_train = f['X_train'][:]
        y_train = f['y_train'][:]
        X_val = f['X_val'][:]
        y_val = f['y_val'][:]
        X_test = f['X_test'][:]
        y_test = f['y_test'][:]
        feature_names = list(f['feature_names'][:])
        scaler_mean = f['scaler_mean'][:]
        scaler_scale = f['scaler_scale'][:]
        metadata_str = f['metadata'][()]

    print(f"  Исходный train: {len(X_train)} сэмплов")
    print(f"  Распределение: {dict(zip(['HOLD','BUY','SELL'], np.bincount(y_train)))}")

    from src.ml.data.augment import TimeSeriesAugmenter

    augmenter = TimeSeriesAugmenter(
        noise_std=0.005,
        warping_sigma=0.2,
        scaling_range=(0.95, 1.05),
        mixup_alpha=0.2,
        dropout_p=0.1,
    )

    X_aug, y_aug = augmenter.augment(
        X_train, y_train,
        methods=['noise', 'warp', 'scale', 'dropout', 'mixup'],
    )

    print(f"  Аугментированный train: {len(X_aug)} сэмплов (×{len(X_aug)//len(X_train)})")
    aug_dist = np.bincount(y_aug) if max(y_aug) < 3 else np.bincount(y_aug, minlength=3)
    print(f"  Распределение: {dict(zip(['HOLD','BUY','SELL'],
          [aug_dist[0] if len(aug_dist) > 0 else 0,
           aug_dist[1] if len(aug_dist) > 1 else 0,
           aug_dist[2] if len(aug_dist) > 2 else 0]))}")

    # Сохраняем аугментированный датасет
    aug_path = SAVED_DIR / 'x5_d1_augmented.h5'
    from src.ml.features.pipeline import save_dataset_to_hdf5
    save_dataset_to_hdf5(
        save_path=str(aug_path),
        X_train=X_aug, y_train=y_aug,
        X_val=X_val, y_val=y_val,
        X_test=X_test, y_test=y_test,
        feature_names=feature_names,
        scaler_mean=scaler_mean,
        scaler_scale=scaler_scale,
        metadata={
            'ticker': 'X5',
            'tf': 'D1',
            'seq_len': 1,
            'augmentation': 'noise+warp+scale+dropout+mixup',
            'augmentation_factor': len(X_aug) // len(X_train),
            'original_train': len(X_train),
            'augmented_train': len(X_aug),
        },
    )

    print(f"  ✅ Аугментированный датасет: {aug_path}")
    return {'X_train': X_aug, 'y_train': y_aug, 'X_val': X_val, 'y_val': y_val,
            'X_test': X_test, 'y_test': y_test}


def approach_2_h1_timeframe():
    """
    Подход 2: Использование H1 таймфрейма вместо D1.

    X5 H1: 7,602 бара → ~5,000+ сэмплов (seq_len=120 = 5 дней).
    """
    print("\n" + "=" * 70)
    print("📊 ПОДХОД 2: H1 ТАЙМФРЕЙМ (X5 H1 → 5,000+ сэмплов)")
    print("=" * 70)

    from src.ml.features.pipeline import create_ml_dataset

    result = create_ml_dataset(
        ticker='X5',
        tf='H1',
        seq_len=120,          # 120 часов = 5 торговых дней
        forecast_horizon=1,   # прогноз на 1 час вперёд
        threshold_pct=0.3,    # порог 0.3% для H1 (меньше, чем для D1)
        target_method='direction',
        val_split=0.15,
        test_split=0.15,
        limit=8000,
        save_path=str(SAVED_DIR / 'x5_h1_dataset.h5'),
    )

    meta = result['metadata']
    n_train = meta['train_samples']
    print(f"  ✅ H1 датасет: {n_train} train сэмплов")
    print(f"  Это в {n_train / 150:.1f}× больше D1 датасета!")

    return result


def approach_3_multi_ticker():
    """
    Подход 3: Multi-ticker pre-training.

    Используем D1 данные нескольких ликвидных MOEX тикеров
    для создания большого разнообразного датасета.
    """
    print("\n" + "=" * 70)
    print("📊 ПОДХОД 3: MULTI-TICKER PRE-TRAINING")
    print("=" * 70)

    # Выбираем тикеры с достаточным количеством данных
    tickers = ['SBER', 'LKOH', 'NVTK', 'MTSS', 'PHOR', 'VTBR', 'MOEX', 'GAZP', 'PLZL']

    from src.ml.features.pipeline import create_ml_dataset

    all_X_train, all_y_train = [], []
    total_features = None
    combined_metadata = {}

    for i, ticker in enumerate(tickers):
        print(f"\n  [{i+1}/{len(tickers)}] {ticker}...")
        try:
            result = create_ml_dataset(
                ticker=ticker,
                tf='D1',
                seq_len=60,
                forecast_horizon=1,
                threshold_pct=0.5,
                target_method='direction',
                val_split=0.0,  # Не делаем val/test — только train для pre-training
                test_split=0.0,
                limit=2000,
                save_path=None,  # Не сохраняем поотдельности
            )

            n = len(result['y_train'])
            all_X_train.append(result['X_train'])
            all_y_train.append(result['y_train'])
            print(f"    +{n} сэмплов")
            combined_metadata[ticker] = n

        except Exception as e:
            print(f"    ⚠️ Ошибка: {e}")

    if not all_X_train:
        print("  ❌ Не удалось загрузить ни одного тикера")
        return None

    # Объединяем
    X_all = np.concatenate(all_X_train, axis=0)
    y_all = np.concatenate(all_y_train, axis=0)

    print(f"\n  Всего: {len(X_all)} сэмплов из {len(tickers)} тикеров")
    print(f"  Распределение классов:")
    for cls, name in [(0, 'HOLD'), (1, 'BUY'), (2, 'SELL')]:
        pct = (y_all == cls).mean() * 100
        print(f"    {name}: {pct:.1f}%")

    # Сохраняем
    mt_path = SAVED_DIR / 'multi_ticker_d1_pretrain.h5'
    import h5py, json
    with h5py.File(mt_path, 'w') as f:
        f.create_dataset('X_train', data=X_all, compression='gzip')
        f.create_dataset('y_train', data=y_all, compression='gzip')
        f.create_dataset('tickers', data=list(combined_metadata.keys()),
                         dtype=h5py.string_dtype())
        metadata_json = json.dumps(combined_metadata, indent=2)
        f.create_dataset('metadata', data=metadata_json, dtype=h5py.string_dtype())

    print(f"  ✅ Multi-ticker датасет: {mt_path} ({os.path.getsize(mt_path)/1024/1024:.1f} MB)")

    return {'X_train': X_all, 'y_train': y_all}


def approach_4_combined():
    """
    Подход 4: Комбинированный — pre-train на H1 всех тикеров,
    fine-tune на X5 D1 с augmentation.
    """
    print("\n" + "=" * 70)
    print("📊 ПОДХОД 4: КОМБИНИРОВАННЫЙ (рекомендуемый)")
    print("=" * 70)

    # 4a: Сначала создаём H1 датасет для X5 (7,602 → ~5,000 сэмплов)
    # 4b: Аугментируем его (×5 → ~25,000 сэмплов)
    # 4c: Для pre-training добавляем D1 других тикеров (×3 → ~40,000 сэмплов)

    print("""
  🏗️ РЕКОМЕНДУЕМАЯ СТРАТЕГИЯ:

  Шаг 1: Создать H1 датасет X5
    python src/ml/features/pipeline.py
    # в коде заменить tf='D1' на tf='H1', seq_len=120

  Шаг 2: Аугментировать H1 датасет (×5)
    python -c "from src.ml.data.augment import augment_dataset_hdf5; ...
               augment_dataset_hdf5('x5_h1_dataset.h5', 'x5_h1_augmented.h5', factor=5)"

  Шаг 3: Pre-train на multi-ticker данных
    python src/ml/expand_dataset.py  # --mode multi_ticker

  Шаг 4: Fine-tune на X5 D1 (150 целевых сэмплов)
    python src/ml/train_ticker.py --ticker X5 --pretrain multi_ticker_d1_pretrain.h5

  Ожидаемый результат:
    - Train: 25,000+ сэмплов (H1 + augmentation) или 10,000+ (multi-ticker)
    - Val:   800+ сэмплов
    - Test:  800+ сэмплов
    - Acc:   45-55% (vs 36% сейчас)
    """)

    return True


if __name__ == '__main__':
    print("=" * 70)
    print("🧠 РАСШИРЕНИЕ ML-ДАТАСЕТА X5 — ВСЕ ПОДХОДЫ")
    print("=" * 70)

    print("\nВыберите режим запуска:")
    print("  1 — Data Augmentation (X5 D1 → ×6)")
    print("  2 — H1 таймфрейм (X5 H1 → ×33 больше данных)")
    print("  3 — Multi-ticker pre-training")
    print("  4 — Комбинированная стратегия (рекомендации)")
    print("  all — Запустить всё последовательно")

    mode = sys.argv[1] if len(sys.argv) > 1 else 'all'
    print(f"\nРежим: {mode}\n")

    if mode in ('1', 'all'):
        approach_1_data_augmentation()

    if mode in ('2', 'all'):
        approach_2_h1_timeframe()

    if mode in ('3', 'all'):
        approach_3_multi_ticker()

    if mode in ('4', 'all'):
        approach_4_combined()

    print("\n" + "=" * 70)
    print("✅ Все подходы продемонстрированы")
    print("=" * 70)
