"""
confirmed_marks.py -- [2026-10-01] The user: the buy signal is in the wrong spot on the charts
(MSGY/MEDS bought at 9:36 before a crash); locate the buy marks properly. DRAFT rules for a
"confirmed" buy mark (to be approved on example charts before re-marking the library):
  1 swing low L at minute i: no lower low in the next `hold_min` (3) minutes
  2 confirmation c: first minute after i (>= i + hold_min) whose close is above the high of
    the low's bar, with still no lower low -> BUY at the close of c, not before 9:35
  3 stop = L - 1c; risk = buy - stop
  4 valid only if, before the stop is hit, price reaches max(2 x risk, 2% of the buy) above the
    buy (reward:risk >= 2 and >= +2%); otherwise not a buy mark
  5 sell mark = highest close after the buy before the stop is hit (or 15:55)
The day's mark = the valid confirmed trade with the biggest gain; a second one after its sell.
No valid trade -> no buy mark that day.
"""
import json
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE)); sys.path.insert(0, "/var/www/screener/trade")
import label_days as LD

HOLD = 3
MIN_RR = 2.0
MIN_GAIN = 2.0
FIRST_BUY_M = 5   # 9:35


def confirmed_trades(B, start=0):
    """All valid confirmed trades starting at index >= start, as (i_low, c, j_sell, gain%)."""
    out = []
    n = len(B)
    i = start
    while i < n - HOLD - 1:
        L = B[i]["l"]
        # swing low: no lower low in the next HOLD bars, and lower than the previous 3
        if any(B[k]["l"] < L for k in range(i + 1, min(i + 1 + HOLD, n))) or \
                any(B[k]["l"] < L for k in range(max(0, i - 3), i)):
            i += 1
            continue
        c = None
        for k in range(i + HOLD, min(i + 60, n)):
            if B[k]["l"] < L:
                break
            if B[k]["c"] > B[i]["h"] and B[k]["m"] >= FIRST_BUY_M and B[k]["m"] <= 345:
                c = k
                break
        if c is None:
            i += 1
            continue
        e = B[c]["c"]; stop = L - 0.01; risk = e - stop
        need = e + max(MIN_RR * risk, e * MIN_GAIN / 100)
        best_j, best_c, ok = None, None, False
        for k in range(c + 1, n):
            if B[k]["m"] > 385 or B[k]["l"] <= stop:
                break
            if B[k]["h"] >= need:
                ok = True
            if best_c is None or B[k]["c"] > best_c:
                best_c, best_j = B[k]["c"], k
        if ok and best_j is not None and best_c > e:
            out.append((i, c, best_j, (best_c / e - 1) * 100, stop))
        i += 1
    return out


DROP_PCT = 1.5    # [2026-10-01, user on LWLG] split a marked trade before a deep drop
DROP_R = 2.0


def split_trades(B):
    """Greedy through the day: take the first valid confirmed trade; while in it, end it at the
    highest close so far when price closes >= max(DROP_PCT %, DROP_R x starting risk) under that
    high AND below VWAP (or at the high before the stop is hit / 15:55); then look for the next
    confirmed trade after it. Only trades that pass the 2x-risk / +2% rule are kept."""
    out, start = [], 0
    n = len(B)
    while True:
        cands = [t for t in confirmed_trades(B, start)]
        if not cands:
            break
        i, c, _, _, stop = min(cands, key=lambda t: t[1])
        e = B[c]["c"]; risk = e - stop
        hi_c, hi_j, end_j = e, c, None
        for k in range(c + 1, n):
            if B[k]["m"] > 385 or B[k]["l"] <= stop:
                end_j = k; break
            if B[k]["c"] > hi_c:
                hi_c, hi_j = B[k]["c"], k
            drop = max(hi_c * DROP_PCT / 100, DROP_R * risk)
            if B[k]["c"] <= hi_c - drop and B[k]["c"] < B[k]["vwap"]:
                end_j = k; break
        gain = (hi_c / e - 1) * 100
        if hi_j > c and hi_c >= e + max(2 * risk, e * 0.02):
            out.append((i, c, hi_j, gain, stop))
        start = max((end_j or n) - 0, c + 1)
        if start >= n - 5:
            break
    return out


def label_confirmed_split(B):
    T = split_trades(B)
    mk = lambda t: {"low_m": B[t[0]]["m"], "entry_m": B[t[1]]["m"], "entry": B[t[1]]["c"], "stop": round(t[4], 4),
                    "exit_m": B[t[2]]["m"], "exit": B[t[2]]["c"], "gain_pct": round(t[3], 2)}
    return {"trades": [mk(t) for t in T]}


def label_confirmed(B):
    T = confirmed_trades(B)
    if not T:
        return {"buy": None, "leg2": None}
    best = max(T, key=lambda t: t[3])
    mk = lambda t: {"low_m": B[t[0]]["m"], "entry_m": B[t[1]]["m"], "entry": B[t[1]]["c"], "stop": round(t[4], 4),
                    "exit_m": B[t[2]]["m"], "exit": B[t[2]]["c"], "gain_pct": round(t[3], 2)}
    later = [t for t in T if t[1] > best[2]]
    leg2 = max(later, key=lambda t: t[3]) if later else None
    return {"buy": mk(best), "leg2": mk(leg2) if leg2 else None}
