"""
Reset all optimization results to a clean slate.

Deletes:
  - results/sweep_database.db          (accumulated sweep rows)
  - results/winners/*.csv              (winner + run-best param files)
  - results/winners/*.txt              (cheatsheets)
  - results/winners/*.pine             (per-preset Pine snippets)

WHEN TO USE
-----------
Run this whenever the trailing stop simulation, scoring formula, data
export, or any other change that invalidates historical comparisons has
been made.  The key rule is:

    Wipe the DB  →  also wipe the winners.

If you wipe the DB without wiping the winners, auto_optimize_loop.py
reads the composite score from the stale winner CSV and uses it as the
GPU pre-filter baseline.  Under a corrected (lower-scoring) simulation
the optimizer can never beat that inflated baseline, so NO new winner
is ever saved — the run produces data in the DB but the winner files
stay permanently frozen at the old (wrong) values.

WHAT IS NOT TOUCHED (by default)
---------------------------------
  - strategies/params/*.json           (param search ranges)
  - data/*.csv                         (TradingView exports)
  - results/oos_dashboard.md           (regenerated by oos_dashboard.py)
  - results/sweep_dashboard.md         (regenerated by mine_sweep_db.py)

Use --reset-params to also restore all weight ranges in params JSONs to
wide defaults (e.g. after changing WFO selection logic, when locks from
a prior bull-biased DB are no longer valid).  Structural locks — params
with {"values": [...]} rather than {"start":..., "stop":..., "step":...}
— are preserved, since those are intentional (condition gates, disabled
signals).  Only range-based weight params are widened.

Usage:
    python3 tools/reset_results.py                        # dry run
    python3 tools/reset_results.py --apply                # delete DB + winners
    python3 tools/reset_results.py --apply --reset-params # also widen param ranges
"""

import argparse
import glob
import json
import os
import sys


RESULTS_DIR  = "results"
WINNERS_DIR  = os.path.join(RESULTS_DIR, "winners")
DB_PATH      = os.path.join(RESULTS_DIR, "sweep_database.db")
PARAMS_GLOB  = "strategies/params/params_strategy_activation_scores_*_*.json"
WIDE_RANGE   = {"start": -100.0, "stop": 100.0, "step": 5.0}


def collect_targets():
    targets = []
    if os.path.exists(DB_PATH):
        targets.append(DB_PATH)
    for pattern in ("*.csv", "*.txt", "*.pine"):
        targets.extend(sorted(glob.glob(os.path.join(WINNERS_DIR, pattern))))
    return targets


def reset_params(apply: bool):
    """
    Restore all range-based weight params to WIDE_RANGE across all active
    params files.  Preserves structural locks ({"values": [...]}) untouched —
    those are intentional (condition gates, regime filter, disabled signals).
    """
    files = sorted(glob.glob(PARAMS_GLOB))
    if not files:
        print("  WARNING: no params files found — nothing to reset.")
        return

    total_widened = 0
    for path in files:
        shortname = os.path.basename(path)
        try:
            with open(path) as f:
                params = json.load(f)
        except Exception as e:
            print(f"  ERROR reading {path}: {e}")
            continue

        widened = []
        for key, val in params.items():
            if not key.startswith("i_w_"):
                continue
            if not isinstance(val, dict):
                continue
            if "values" in val:
                continue  # structural lock — preserve
            current_range = (val.get("start"), val.get("stop"))
            if current_range != (WIDE_RANGE["start"], WIDE_RANGE["stop"]):
                widened.append(key)
                if apply:
                    params[key] = WIDE_RANGE.copy()

        if widened:
            total_widened += len(widened)
            verb = "Widened" if apply else "Would widen"
            print(f"  {shortname}: {verb} {len(widened)} param(s): {', '.join(widened)}")
            if apply:
                with open(path, "w") as f:
                    json.dump(params, f, indent=2)
        else:
            print(f"  {shortname}: already wide — no changes")

    verb = "Widened" if apply else "Would widen"
    print(f"\n  {verb} {total_widened} range(s) across {len(files)} params file(s).")
    if not apply:
        print("  Re-run with --apply --reset-params to write changes.")


def main():
    parser = argparse.ArgumentParser(
        description="Reset all optimization results (DB + winner files)."
    )
    parser.add_argument(
        "--apply", action="store_true",
        help="Actually delete files (default is dry run).",
    )
    parser.add_argument(
        "--reset-params", action="store_true",
        help="Also restore all weight param ranges to wide defaults (preserves structural locks).",
    )
    args = parser.parse_args()

    if not os.path.isdir(WINNERS_DIR):
        os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    if not os.path.isdir(WINNERS_DIR):
        print("ERROR: cannot locate results/winners/ directory.", file=sys.stderr)
        sys.exit(1)

    targets = collect_targets()

    if not targets:
        print("Nothing to delete — DB and winners already clean.")
    else:
        print(f"{'[DRY RUN] Would delete' if not args.apply else 'Deleting'} {len(targets)} file(s):")
        for p in targets:
            print(f"  {p}")
        if args.apply:
            for p in targets:
                os.remove(p)
            print(f"\n✓  Deleted {len(targets)} file(s). Results are now clean.")

    if args.reset_params:
        print(f"\n{'[DRY RUN] Param range reset:' if not args.apply else 'Resetting param ranges:'}")
        reset_params(apply=args.apply)

    if not args.apply:
        flags = "--apply" + (" --reset-params" if args.reset_params else "")
        print(f"\nRe-run with {flags} to apply.")
        return

    print("\n   Next steps:")
    print("   1. python3 tools/sync_params.py   # verify all files in sync")
    print("   2. python3 run_btc.py --hours 0.5  # short validation run before marathon")


if __name__ == "__main__":
    main()
