#!/usr/bin/env python3
"""One-time repair: Reprocess days with duration_us=0 orphan bars (Issue #275).

Root cause: `reset_at_ouroboros()` in the Rust processor returned orphan bars
WITHOUT calling `compute_microstructure_features()`. All 10 microstructure
columns are zero for ~12,959 orphan bars across BTCUSDT (2,980 days) and
ETHUSDT (238 days).

This script:
1. Queries ClickHouse for all (symbol, day) pairs with duration_us=0 bars
2. Bulk-deletes ALL bars for those days (kintsugi reprocesses full days atomically)
3. Kintsugi rediscovers the gaps and reprocesses with the fixed Rust processor

After running, start kintsugi daemon — it will see the gaps and heal them with
the fixed Rust processor that now calls compute_microstructure_features() on orphans.

Usage:
    # Dry run (default): show what would be deleted
    python scripts/repair_orphan_microstructure.py

    # Execute: delete orphan-day bars, then run kintsugi
    python scripts/repair_orphan_microstructure.py --execute

    # Limit to specific symbol
    python scripts/repair_orphan_microstructure.py --execute --symbol BTCUSDT
"""
from __future__ import annotations

import argparse
import logging
import sys

import clickhouse_connect

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)

_CH_HOST = "localhost"
_CH_PORT = 8123


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Repair orphan bars with duration_us=0 (Issue #275)",
    )
    parser.add_argument(
        "--execute", action="store_true",
        help="Actually delete bars (default: dry run)",
    )
    parser.add_argument(
        "--symbol", type=str, default=None,
        help="Limit to specific symbol (e.g., BTCUSDT)",
    )
    args = parser.parse_args()

    client = clickhouse_connect.get_client(host=_CH_HOST, port=_CH_PORT)

    # --- Step 1: Summary of affected bars ---
    symbol_filter = f"AND symbol = '{args.symbol}'" if args.symbol else ""

    summary = client.query(f"""
    SELECT
        symbol,
        threshold_decimal_bps,
        count() AS orphan_bars,
        uniqExact(toDate(open_time_ms / 1000, 'UTC')) AS affected_days,
        min(toDate(open_time_ms / 1000, 'UTC')) AS earliest,
        max(toDate(open_time_ms / 1000, 'UTC')) AS latest
    FROM opendeviationbar_cache.open_deviation_bars FINAL
    WHERE duration_us = 0 {symbol_filter}
    GROUP BY symbol, threshold_decimal_bps
    ORDER BY symbol, threshold_decimal_bps
    """)

    if not summary.result_rows:
        logger.info("No affected bars found — nothing to repair")
        sys.exit(0)

    logger.info("=== Issue #275 Orphan Microstructure Repair ===")
    logger.info("")
    logger.info("Affected bars (duration_us=0, all microstructure columns zero):")
    total_orphans = 0
    for row in summary.result_rows:
        sym, thresh, count, days, earliest, latest = row
        total_orphans += count
        logger.info("  %s@%d: %d orphan bars across %d days (%s → %s)", sym, thresh, count, days, earliest, latest)
    logger.info("  Total: %d orphan bars", total_orphans)

    # --- Step 2: Count total bars in affected days (what DELETE would remove) ---
    impact = client.query(f"""
    SELECT
        symbol,
        count() AS total_bars_in_affected_days,
        uniqExact(toDate(open_time_ms / 1000, 'UTC')) AS days
    FROM opendeviationbar_cache.open_deviation_bars FINAL
    WHERE (symbol, toDate(open_time_ms / 1000, 'UTC')) IN (
        SELECT symbol, toDate(open_time_ms / 1000, 'UTC')
        FROM opendeviationbar_cache.open_deviation_bars FINAL
        WHERE duration_us = 0 {symbol_filter}
    )
    GROUP BY symbol
    ORDER BY symbol
    """)

    logger.info("")
    logger.info("Impact: DELETE all bars in affected days (kintsugi reprocesses full days):")
    total_delete = 0
    for row in impact.result_rows:
        sym, bar_count, day_count = row
        total_delete += bar_count
        logger.info("  %s: %d bars across %d days would be deleted + reprocessed", sym, bar_count, day_count)
    logger.info("  Total: %d bars to delete + reprocess", total_delete)

    if not args.execute:
        logger.info("")
        logger.info("DRY RUN — add --execute to delete bars and trigger kintsugi repair")
        return

    # --- Step 3: Execute bulk delete per symbol ---
    logger.info("")
    logger.info("Executing bulk delete...")

    symbols = list({row[0] for row in summary.result_rows})
    for sym in sorted(symbols):
        # Get the affected days for this symbol as a list
        days_result = client.query(f"""
        SELECT DISTINCT toDate(open_time_ms / 1000, 'UTC') AS day
        FROM opendeviationbar_cache.open_deviation_bars FINAL
        WHERE duration_us = 0 AND symbol = '{sym}'
        ORDER BY day
        """)
        days = [row[0] for row in days_result.result_rows]

        # Bulk delete: all bars in affected days for this symbol
        # Uses lightweight DELETE FROM (not ALTER TABLE DELETE — avoids mutation storms #269)
        days_str = ", ".join(f"'{d}'" for d in days)
        delete_query = f"""
        DELETE FROM opendeviationbar_cache.open_deviation_bars
        WHERE symbol = '{sym}'
          AND toDate(open_time_ms / 1000, 'UTC') IN ({days_str})
        """
        client.command(delete_query)
        logger.info("  %s: deleted bars for %d days", sym, len(days))

    logger.info("")
    logger.info("Delete complete. Kintsugi will rediscover these as Stathera gaps.")
    logger.info("")
    logger.info("Next steps:")
    logger.info("  1. Ensure the new wheel (with #275 fix) is installed:")
    logger.info("     pip install --upgrade opendeviationbar")
    logger.info("  2. Restart kintsugi to trigger gap discovery:")
    logger.info("     systemctl --user restart opendeviationbar-kintsugi")
    logger.info("  3. Monitor repair progress:")
    logger.info("     journalctl --user -u opendeviationbar-kintsugi -f")


if __name__ == "__main__":
    main()
