"""Verify sequential line renumbering:
1. Lines get L1, L2, L3... in creation order
2. Deleting a middle line renumbers so there are no gaps
3. Custom labels are preserved and shown in the visual tag
4. CSV export keeps the L-number in Pin and custom name in Label
"""
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_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]);
"""


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 4 lines: L1..L4 in creation order
        for i, (sx, sy, ex, ey) in enumerate([(0.1,0.1,0.3,0.2),(0.2,0.3,0.4,0.4),(0.3,0.5,0.5,0.6),(0.4,0.7,0.6,0.8)]):
            d.execute_script(DRAW_JS, sx, sy, ex, ey, 50 + i)
            time.sleep(0.5)
            d.execute_script('document.querySelector(".sheet-minimal [data-action=closesheet]").click()')
            time.sleep(0.3)

        print("nums after 4 draws:", 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, label: l.label})));
        """))

        # Give line 2 a custom label (survives the deletion of line 3)
        d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            sk.lines[1].label = 'Rise';
            T.saveState(); T.render();
        """)
        time.sleep(0.4)

        print("after custom label:", 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, label: l.label})));
        """))

        # Delete line 3 (index 2) via the deleteLine path
        d.execute_script("""
            const T=window.__test;
            const job = T.state.jobs[0]; const sk = job.sketches[0];
            const victim = sk.lines[2];
            window.confirm = () => true;
            // call the internal delete line helper via UI action path
            const el = document.createElement('button');
            el.dataset.action = 'delpin'; el.dataset.sketch = sk.id; el.dataset.pin = victim.id;
            T.ui.pinModal = { sketchId: sk.id, pinId: victim.id, kind: 'line' };
            document.body.appendChild(el);
            el.click();
            el.remove();
            return JSON.stringify(sk.lines.map(l => ({num: l.num, label: l.label})));
        """)
        time.sleep(0.5)

        print("nums after delete L3:", 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, label: l.label})));
        """))

        # Verify visual tag for the line that was L4 (now L3)
        print("lineTag check:", d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            // lineTag is internal; check via the placeholder path by rendering the sheet
            return JSON.stringify(sk.lines.map((l, i) => ({num: l.num, label: l.label, idx: i})));
        """))

        # Verify CSV export rows
        print("csv rows:", d.execute_script("""
            const T=window.__test; const job=T.state.jobs[0]; const sk=job.sketches[0];
            const rows = [["Job","Sketch","Pin","Label","Value","Note"]];
            const items = sk.lines;
            items.forEach(p => {
              const tag = 'L' + p.num;
              rows.push([job.name, sk.name, tag, (p.label||'').trim() || tag, '0', p.note||'']);
            });
            return JSON.stringify(rows);
        """))

        # Canvas tag must be the L-number even with a custom label set. The
        # line with label 'Rise' is L2, so its canvas tag must be 'L2'.
        print("canvas tag for Rise line:", d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            const line = sk.lines[1]; // the one labeled 'Rise'
            return JSON.stringify({label: line.label, num: line.num, canvasTag: T.lineTag(sk, line)});
        """))

        # Info sheet: the name input VALUE is the custom label, placeholder is
        # the L-number. Open the line's sheet and check.
        d.execute_script("""
            const T=window.__test; const sk=T.state.jobs[0].sketches[0];
            const line = sk.lines[1];
            T.ui.pinModal = { sketchId: sk.id, pinId: line.id, kind: 'line' };
            T.render();
        """)
        time.sleep(0.6)
        print("info sheet name field:", d.execute_script("""
            const el = document.querySelector('.sheet input[data-field="pinlabel"]');
            return el ? JSON.stringify({value: el.value, placeholder: el.placeholder}) : 'missing';
        """))

        # Undo should remove the last line (L3) and renumber to L1, L2
        d.execute_script('document.querySelector("[data-action=undo-line]").click()')
        time.sleep(0.4)
        print("after undo:", 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, label: l.label})));
        """))
        # Redo should restore it as L3
        d.execute_script('document.querySelector("[data-action=redo-line]").click()')
        time.sleep(0.4)
        print("after redo:", 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, label: l.label})));
        """))

        # Migration: seed legacy lines without num, save, reload page, verify
        # loadState assigns sequential numbers.
        d.execute_script("""
            const T=window.__test;
            const sk = T.state.jobs[0].sketches[0];
            sk.lines.forEach(l => { delete l.num; });
            T.saveState();
        """)
        d.refresh()
        time.sleep(2)
        print("migration after reload:", 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, label: l.label})));
        """))

    finally:
        d.quit()


if __name__ == "__main__":
    main()