"""Test rectangle drawing and long-press-copy-decimal-value:
1. Rect mode: drawing a diagonal creates 4 lines forming a rectangle with
   equal left/right sides and equal top/bottom sides.
2. Long-press the orange dimension value (.preview-value) copies its decimal
   value (in inches) to the clipboard.
"""
import os, sys, time, json
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service

HERE = os.path.dirname(os.path.abspath(__file__))
GECKODRIVER = os.path.join(HERE, "geckodriver.exe")
BASE = "http://localhost:8000/field-capture.html?debug"

def draw_line(d, sx, sy, ex, ey, pid):
    d.execute_script("""
        return (function(sx, sy, ex, ey, pid){
          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 toC = (nx, ny) => ({ x: left + v.tx + nx*w*v.scale, y: top + v.ty + ny*h*v.scale });
          const s = toC(sx, sy), e = toC(ex, ey);
          const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
          canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: pid, clientX: s.x, clientY: s.y})));
          for (let i = 1; i <= 5; i++) canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: pid, clientX: s.x+(e.x-s.x)*i/5, clientY: s.y+(e.y-s.y)*i/5})));
          canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: pid, clientX: e.x, clientY: e.y})));
        })(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    """, sx, sy, ex, ey, pid)
    time.sleep(0.5)
    try:
        d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
        time.sleep(0.3)
    except Exception:
        pass

def get_lines(d):
    return json.loads(d.execute_script(
        "const T=window.__test; const sk=T.state.jobs[0].sketches[0]; return JSON.stringify(sk.lines.map(l=>({start:l.start,end:l.end})));"
    ))

def main():
    opts = Options()
    opts.headless = False
    svc = Service(executable_path=GECKODRIVER)
    d = webdriver.Firefox(options=opts, service=svc)
    d.set_window_size(480, 900)
    try:
        d.get(BASE)
        time.sleep(2)
        d.find_element(By.CSS_SELECTOR, '[data-action="newjob"]').click()
        time.sleep(0.8)
        d.find_element(By.CSS_SELECTOR, '[data-action="opensketch"]').click()
        time.sleep(0.8)
        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)

        # ---- Rectangle test ----
        # Turn Rect mode ON.
        d.execute_script('const T=window.__test; T.ui.rectMode=true; T.render();')
        time.sleep(0.3)
        # Draw a diagonal from (0.2,0.2) to (0.6,0.5). Should make 4 lines.
        draw_line(d, 0.2, 0.2, 0.6, 0.5, 31)
        lines = get_lines(d)
        print("rect lines:", json.dumps(lines))
        assert len(lines) == 4, f"expected 4 lines, got {len(lines)}"
        # Sorted corners: x1=0.2,x2=0.6, y1=0.2,y2=0.5.
        # Side lengths: top/bottom = 0.4, left/right = 0.3.
        def seglen(a, b):
            return abs(round(((b["x"]-a["x"])**2 + (b["y"]-a["y"])**2) ** 0.5, 6))
        lengths = sorted(seglen(l["start"], l["end"]) for l in lines)
        print("side lengths:", lengths)
        ok1 = lengths[0] == lengths[1] == 0.3 and lengths[2] == lengths[3] == 0.4
        print("PASS rectangle 4 equal-pair sides" if ok1 else "FAIL rectangle sides")
        # All four lines must be axis-aligned (horizontal or vertical).
        ok2 = True
        for l in lines:
            dx = abs(l["end"]["x"] - l["start"]["x"])
            dy = abs(l["end"]["y"] - l["start"]["y"])
            if min(dx, dy) > 1e-6:
                ok2 = False
        print("PASS rectangle axis-aligned" if ok2 else "FAIL rectangle axis-aligned")
        # Turn Rect mode OFF.
        d.execute_script('const T=window.__test; T.ui.rectMode=false; T.render();')
        time.sleep(0.3)

        # ---- Copy-decimal-value test ----
        # Open the first rectangle line's measurement sheet.
        d.execute_script("""
            const T = window.__test;
            const sk = T.state.jobs[0].sketches[0];
            const l = sk.lines[0];
            l.feet = 2; l.inches = 3; l.sixteenths = 8; l.quickTouched = true;
            T.saveState();
            T.ui.pinModal = { sketchId: sk.id, pinId: l.id, kind: 'line' };
            T.ui.modalMode = 'quick';
            T.render();
        """)
        time.sleep(0.5)
        # The quick sheet shows the orange preview-value. Long-press it and
        # verify the clipboard gets the decimal value (2'3-1/2" = 27.5).
        ok3 = d.execute_script("""
            const T = window.__test;
            const el = document.getElementById('quickPreview');
            if (!el) return 'NO_PREVIEW';
            return T.lineDecimalInches(T.state.jobs[0].sketches[0].lines[0]);
        """)
        print("decimal value:", ok3)
        ok3 = ok3 == "27.5"
        print("PASS decimal value computed" if ok3 else "FAIL decimal value computed")

        # Simulate a long-press on the preview and check clipboard via the app
        # copy path (read back what copyText wrote).
        ok4 = d.execute_script("""
            const el = document.getElementById('quickPreview');
            if (!el) return false;
            // Stub the global copyText to capture rather than hit the real
            // clipboard, since the long-press handler calls the global copyText.
            window.__capturedCopy = null;
            window.copyText = (t) => { window.__capturedCopy = t; return Promise.resolve(); };
            const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
            const r = el.getBoundingClientRect();
            const cx = r.left + r.width/2, cy = r.top + r.height/2;
            el.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: 77, clientX: cx, clientY: cy})));
            // Wait past the 420ms hold, then release.
            setTimeout(() => {
              el.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: 77, clientX: cx, clientY: cy})));
            }, 700);
            return true;
        """)
        time.sleep(1.2)
        captured = d.execute_script("return window.__capturedCopy;")
        print("captured copy text:", captured)
        ok4 = captured == "27.5"
        print("PASS long-press copies decimal" if ok4 else "FAIL long-press copies decimal")

        if ok1 and ok2 and ok3 and ok4:
            print("ALL RECT+COPY TESTS PASSED")
        else:
            print("SOME RECT+COPY TESTS FAILED")
    finally:
        d.quit()

if __name__ == "__main__":
    main()