"""Test tag-vs-endpoint grab refinement: when a touch is in range of BOTH a
line's tag (midpoint, copy-drag) and an endpoint (move), the closer feature
wins. Previously the endpoint always won because it was checked first.

Setup mirrors _test_endpoint.py: create job/sketch, inject a photo, draw a
SHORT line (so the tag's 44px zone overlaps the endpoints' 30px zones), then
probe the pointerdown decision.
"""
import sys, time
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service

opts = Options()
svc = Service(executable_path="geckodriver.exe")
d = webdriver.Firefox(options=opts, service=svc)
d.set_window_size(480, 900)

def setup():
    d.get("http://localhost:8000/field-capture.html?debug")
    time.sleep(2)
    d.execute_script('document.querySelector("[data-action=newjob]").click()')
    time.sleep(0.6)
    d.execute_script('document.querySelector("[data-action=opensketch]").click()')
    time.sleep(0.6)
    d.execute_script("""
        const T = window.__test;
        const job = T.state.jobs[0]; const sk = job.sketches[0];
        const c = document.createElement('canvas'); c.width=200; c.height=150;
        const ctx = c.getContext('2d'); ctx.fillStyle='#ccc'; ctx.fillRect(0,0,200,150);
        sk.photo = { dataUrl: c.toDataURL('image/jpeg', 0.8), width: 200, height: 150 };
        T.saveState();
    """)
    d.execute_script(
        "const T=window.__test; T.ui.screen='sketch'; T.ui.sketchId=T.state.jobs[0].sketches[0].id; T.render();"
    )
    time.sleep(0.6)
    d.execute_script("""
        const canvas = document.getElementById('sketchCanvas');
        canvas.setPointerCapture = function(){};
        canvas.releasePointerCapture = function(){};
        canvas.hasPointerCapture = function(){ return false; };
        const shell = document.querySelector('.sketch-shell');
        const sr = shell.getBoundingClientRect();
        const left = sr.left + shell.clientLeft, top = sr.top + shell.clientTop;
        const view = document.getElementById('sketchView');
        const T = window.__test;
        const v = T.ui.viewState[T.ui.sketchId] || { scale: 1, tx: 0, ty: 0 };
        const w = view.offsetWidth, h = view.offsetHeight;
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        const s = { x: left + v.tx + 0.3*w*v.scale, y: top + v.ty + 0.3*h*v.scale }, e = { x: left + v.tx + 0.4*w*v.scale, y: top + v.ty + 0.4*h*v.scale };
        canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: 5, clientX: s.x, clientY: s.y})));
        for (let i = 1; i <= 4; i++) canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: 5, clientX: s.x+(e.x-s.x)*i/4, clientY: s.y+(e.y-s.y)*i/4})));
        canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: 5, clientX: e.x, clientY: e.y})));
    """)
    time.sleep(0.5)
    d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
    time.sleep(0.4)

def probe(lx, ly):
    """Dispatch a pointerdown at normalized coords; return the armed longPress mode."""
    return d.execute_script("""
        return (function(lx, ly){
        const canvas = document.getElementById('sketchCanvas');
        canvas.setPointerCapture = function(){};
        canvas.releasePointerCapture = function(){};
        canvas.hasPointerCapture = function(){ return false; };
        const T = window.__test;
        const shell = document.querySelector('.sketch-shell');
        const sr = shell.getBoundingClientRect();
        const view = document.getElementById('sketchView');
        const v = T.ui.viewState[T.ui.sketchId] || { scale: 1, tx: 0, ty: 0 };
        const w = view.offsetWidth, h = view.offsetHeight;
        const x = sr.left + shell.clientLeft + v.tx + lx*w*v.scale;
        const y = sr.top + shell.clientTop + v.ty + ly*h*v.scale;
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: 9, clientX: x, clientY: y})));
        const mode = T.ui.longPress ? T.ui.longPress.mode : null;
        // Clean up so the armed long-press doesn't bleed into the next probe.
        T.ui.longPress = null;
        return mode;
        })(arguments[0], arguments[1]);
    """, lx, ly)

try:
    setup()
    # Short line from (0.3,0.3) to (0.4,0.4). Midpoint = (0.35,0.35).
    # Probe 1: touch exactly at the midpoint -> must arm tag.
    m = probe(0.35, 0.35)
    print("midpoint  ->", m, "EXPECT tag")
    assert m == "tag", f"expected tag, got {m}"

    # Probe 2: touch at the start endpoint (0.3,0.3) -> must arm endpoint.
    m = probe(0.3, 0.3)
    print("start end ->", m, "EXPECT endpoint")
    assert m == "endpoint", f"expected endpoint, got {m}"

    # Probe 3: touch at the end endpoint (0.4,0.4) -> must arm endpoint.
    m = probe(0.4, 0.4)
    print("end end   ->", m, "EXPECT endpoint")
    assert m == "endpoint", f"expected endpoint, got {m}"

    # Probe 4: touch at a point in BOTH zones but CLOSER to the tag than to
    # either endpoint. Midpoint (0.35,0.35) vs start endpoint (0.3,0.3). A
    # point at (0.34,0.34) is ~0.014 from the tag but ~0.057 from the start
    # endpoint, so the tag must win even though the endpoint is in range.
    m = probe(0.34, 0.34)
    print("near tag  ->", m, "EXPECT tag")
    assert m == "tag", f"expected tag, got {m}"

    # Probe 5: touch in BOTH zones but CLOSER to the start endpoint. A point
    # at (0.31,0.31) is ~0.057 from the tag and ~0.014 from the start endpoint,
    # so the endpoint must win.
    m = probe(0.31, 0.31)
    print("near start->", m, "EXPECT endpoint")
    assert m == "endpoint", f"expected endpoint, got {m}"

    print("ALL TAG-GRAB TESTS PASSED")
finally:
    d.quit()