"""Test endpoint-adjust: long-press near endpoint -> white dots -> drag endpoint;
long-press near body -> drag moves whole line keeping orientation."""
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)
    # Draw a line from (0.2,0.3) to (0.5,0.4)
    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.2*w*v.scale, y: top + v.ty + 0.3*h*v.scale }, e = { x: left + v.tx + 0.5*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 pt_to_client(lx, ly):
    """Normalized sketch coords -> client coords at scale 1."""
    return d.execute_script("""
        return (function(lx, ly){
          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;
          return { x: left + v.tx + lx * w * v.scale, y: top + v.ty + ly * h * v.scale };
        })(arguments[0], arguments[1]);
    """, lx, ly)

def hold_and_drag(start_client, end_client, pointer_id, hold_s=0.6):
    """Hold at start_client for hold_s, then drag to end_client."""
    d.execute_script("""
        const canvas = document.getElementById('sketchCanvas');
        canvas.setPointerCapture = function(){};
        canvas.releasePointerCapture = function(){};
        canvas.hasPointerCapture = function(){ return false; };
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: arguments[0], clientX: arguments[1], clientY: arguments[2]})));
    """, pointer_id, start_client["x"], start_client["y"])
    time.sleep(hold_s)
    d.execute_script("""
        const canvas = document.getElementById('sketchCanvas');
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        for (let i = 1; i <= 5; i++) canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: arguments[0], clientX: arguments[1] + (arguments[3]-arguments[1])*i/5, clientY: arguments[2] + (arguments[4]-arguments[2])*i/5})));
        canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: arguments[0], clientX: arguments[3], clientY: arguments[4]})));
    """, pointer_id, start_client["x"], start_client["y"], end_client["x"], end_client["y"])

def hold_only(client, pointer_id, hold_s=0.6):
    """Hold at client point for hold_s, then pointerup without moving. Returns modal state."""
    d.execute_script("""
        const canvas = document.getElementById('sketchCanvas');
        canvas.setPointerCapture = function(){};
        canvas.releasePointerCapture = function(){};
        canvas.hasPointerCapture = function(){ return false; };
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: arguments[0], clientX: arguments[1], clientY: arguments[2]})));
    """, pointer_id, client["x"], client["y"])
    time.sleep(hold_s)
    return d.execute_script("""
        const canvas = document.getElementById('sketchCanvas');
        const T = window.__test;
        const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
        canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: arguments[0], clientX: arguments[1], clientY: arguments[2]})));
        return JSON.stringify({pinModal: T.ui.pinModal, longPress: T.ui.longPress});
    """, pointer_id, client["x"], client["y"])

def get_lines():
    return d.execute_script("""
        const T=window.__test; const sk=T.state.jobs[0].sketches[0];
        return JSON.stringify(sk.lines.map(l => ({id: l.id, start: l.start, end: l.end})));
    """)

try:
    setup()
    print("line before:", get_lines())

    # --- Test 1: endpoint drag ---
    # Hold on the START endpoint (0.2, 0.3), then drag it to (0.1, 0.5)
    s_endpoint = pt_to_client(0.2, 0.3)
    e_endpoint = pt_to_client(0.1, 0.5)
    hold_and_drag(s_endpoint, e_endpoint, 7)
    time.sleep(0.3)
    print("after endpoint drag (expect start->0.1,0.5):", get_lines())

    # --- Test 2: whole-line drag (body grab) ---
    # After the endpoint drag the line runs (0.1,0.5) -> (0.5,0.4). Grab the
    # body at 25% along: (0.2, 0.475) — away from the tag (midpoint) and the
    # endpoints. Drag to (0.4, 0.575) -> shift by (+0.2, +0.1).
    body = pt_to_client(0.2, 0.475)
    body_end = pt_to_client(0.4, 0.575)
    hold_and_drag(body, body_end, 8)
    time.sleep(0.3)
    print("after body drag (expect both shifted by +0.2,+0.1):", get_lines())

    # --- Test 3: hold on endpoint without drag -> opens the edit modal
    # (endpoint-armed long press with no drag = tap)
    # After the body drag the line is (0.3,0.6) -> (0.7,0.5).
    ep = pt_to_client(0.3, 0.6)
    print("hold-only on endpoint:", hold_only(ep, 9))

    # --- Test 4: hold on tag without drag -> opens modal (existing behavior)
    # Tag is at the midpoint (0.5, 0.55).
    tag = pt_to_client(0.5, 0.55)
    print("hold-only on tag:", hold_only(tag, 10))

finally:
    d.quit()