"""Verify copy-drag from line tag:
1. Long-press on a line's TAG then drag copies the line (same dims, new id/num)
2. The copy is independent (changing one doesn't change the other)
3. Long-press on the tag without drag opens the edit modal
4. Drawing near a line (not the tag) still draws a new line
"""
import os
import sys
import time

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"

# Draw a line via synthetic pointer events (untransformed shell coords).
DRAW_JS = """
return (function(startX, startY, endX, endY, pointerId){
  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 toClient = (nx, ny) => ({ x: left + v.tx + nx * w * v.scale, y: top + v.ty + ny * h * v.scale });
  const s = toClient(startX, startY), e = toClient(endX, endY);
  const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
  canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: pointerId, clientX: s.x, clientY: s.y})));
  for (let i = 1; i <= 5; i++) canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: pointerId, 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: pointerId, clientX: e.x, clientY: e.y})));
  return 'drawn';
})(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
"""

# Long-press on the TAG of line index then drag — split into separate steps so
# the browser event loop can fire the long-press timer between them.
def copy_drag(d, line_idx, dx, dy, pointer_id, hold_s=0.6):
    # pointerdown at the tag midpoint
    d.execute_script("""
        return (function(lineIdx, pointerId){
          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 sk = T.state.jobs[0].sketches[0];
          const line = sk.lines[lineIdx];
          const mx = left + v.tx + ((line.start.x + line.end.x) / 2) * w * v.scale;
          const my = top + v.ty + ((line.start.y + line.end.y) / 2) * h * v.scale;
          const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
          canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: pointerId, clientX: mx, clientY: my})));
          return 'down';
        })(arguments[0], arguments[1]);
    """, line_idx, pointer_id)
    time.sleep(hold_s)
    # drag away
    return d.execute_script("""
        return (function(lineIdx, dx, dy, pointerId){
          const canvas = document.getElementById('sketchCanvas');
          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 sk = T.state.jobs[0].sketches[0];
          const line = sk.lines[lineIdx];
          const mx = left + v.tx + ((line.start.x + line.end.x) / 2) * w * v.scale;
          const my = top + v.ty + ((line.start.y + line.end.y) / 2) * h * v.scale;
          const ex = left + v.tx + ((line.start.x + line.end.x) / 2 + dx) * w * v.scale;
          const ey = top + v.ty + ((line.start.y + line.end.y) / 2 + dy) * h * v.scale;
          const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
          for (let i = 1; i <= 6; i++) canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: pointerId, clientX: mx+(ex-mx)*i/6, clientY: my+(ey-my)*i/6})));
          canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: pointerId, clientX: ex, clientY: ey})));
          const sk2 = T.state.jobs[0].sketches[0];
          return JSON.stringify(sk2.lines.map(l => ({id: l.id, num: l.num, start: l.start, end: l.end, label: l.label, feet: l.feet, inches: l.inches})));
        })(arguments[0], arguments[1], arguments[2], arguments[3]);
    """, line_idx, dx, dy, pointer_id)


# Long-press on the tag WITHOUT dragging: should open the edit modal.
def hold_to_edit(d, line_idx, pointer_id, hold_s=0.6):
    d.execute_script("""
        return (function(lineIdx, pointerId){
          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 sk = T.state.jobs[0].sketches[0];
          const line = sk.lines[lineIdx];
          const mx = left + v.tx + ((line.start.x + line.end.x) / 2) * w * v.scale;
          const my = top + v.ty + ((line.start.y + line.end.y) / 2) * h * v.scale;
          const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
          canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: pointerId, clientX: mx, clientY: my})));
          return 'down';
        })(arguments[0], arguments[1]);
    """, line_idx, pointer_id)
    time.sleep(hold_s)
    return d.execute_script("""
        return (function(lineIdx, pointerId){
          const canvas = document.getElementById('sketchCanvas');
          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 sk = T.state.jobs[0].sketches[0];
          const line = sk.lines[lineIdx];
          const mx = left + v.tx + ((line.start.x + line.end.x) / 2) * w * v.scale;
          const my = top + v.ty + ((line.start.y + line.end.y) / 2) * h * v.scale;
          const opts = { bubbles: true, cancelable: true, pointerType: 'touch', isPrimary: true };
          canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: pointerId, clientX: mx, clientY: my})));
          return JSON.stringify({pinModal: T.ui.pinModal, overlay: !!document.querySelector('.sheet-overlay')});
        })(arguments[0], arguments[1]);
    """, line_idx, pointer_id)


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 = '#cccccc'; 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.8)

        # Draw a single source line
        d.execute_script(DRAW_JS, 0.2, 0.3, 0.5, 0.4, 11)
        time.sleep(0.5)
        d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
        time.sleep(0.3)

        # Give it a dimension value (2 ft) and a label
        d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            const l = sk.lines[0];
            l.feet = 2; l.label = 'Rise';
            T.saveState(); T.render();
        """)
        time.sleep(0.4)

        # 1. Copy-drag: hold 600ms on the tag, then drag by (0.2, 0.15)
        print("copy-drag:", copy_drag(d, 0, 0.2, 0.15, 21))
        time.sleep(0.5)
        # Close the copy's modal
        d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
        time.sleep(0.3)

        # 2. Verify the copy: same dims/label, new id/num, offset geometry
        print("lines after copy:", 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, num: l.num, label: l.label, feet: l.feet, start: l.start, end: l.end})));
        """))

        # 3. Independence: change the copy's value, source must be unchanged
        d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            sk.lines[1].feet = 5; T.saveState();
        """)
        print("independence:", d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            return JSON.stringify(sk.lines.map(l => ({num: l.num, feet: l.feet})));
        """))

        # 4. Long-press tag without drag opens edit modal
        print("hold-to-edit:", hold_to_edit(d, 0, 31))
        time.sleep(0.3)
        d.execute_script('document.querySelector(".sheet [data-action=closesheet]").click()')
        time.sleep(0.3)

        # 5. Draw a new line near the line but NOT at the tag — should create
        #    a new line, not copy.
        before = d.execute_script("return window.__test.state.jobs[0].sketches[0].lines.length;")
        d.execute_script(DRAW_JS, 0.6, 0.6, 0.8, 0.7, 41)
        time.sleep(0.5)
        after = d.execute_script("return window.__test.state.jobs[0].sketches[0].lines.length;")
        print("draw near line (not tag): before=%s after=%s" % (before, after))
        d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
        time.sleep(0.3)

    finally:
        d.quit()


if __name__ == "__main__":
    main()