import {
	Editor,
	StateNode,
	TLNoteShape,
	TLPointerEventInfo,
	TLShapeId,
	Vec,
	createShapeId,
	maybeSnapToGrid,
} from '@tldraw/editor'

import { startEditingShapeWithRichText } from '../../../tools/SelectTool/selectHelpers'
import {
	NOTE_ADJACENT_POSITION_SNAP_RADIUS,
	getAvailableNoteAdjacentPositions,
} from '../noteHelpers'

export class Pointing extends StateNode {
	static override id = 'pointing'

	dragged = false

	info = {} as TLPointerEventInfo

	markId = ''

	shape = {} as TLNoteShape

	override onEnter() {
		const { editor } = this

		const id = createShapeId()
		this.markId = editor.markHistoryStoppingPoint(`creating_note:${id}`)

		// Check for note pits; if the pointer is close to one, place the note centered on the pit
		const center = this.editor.inputs.getOriginPagePoint().clone()
		const offset = getNoteShapeAdjacentPositionOffset(
			this.editor,
			center,
			this.editor.getResizeScaleFactor()
		)
		if (offset) {
			center.sub(offset)
		}

		// Allow this to trigger the max shapes reached alert
		const shape = createNoteShape(this.editor, id, center)
		if (shape) {
			this.shape = shape
		} else {
			this.cancel()
		}
	}

	override onPointerMove(info: TLPointerEventInfo) {
		if (this.editor.inputs.getIsDragging()) {
			this.editor.setCurrentTool('select.translating', {
				...info,
				target: 'shape',
				shape: this.shape,
				onInteractionEnd: 'note',
				isCreating: true,
				creatingMarkId: this.markId,
				onCreate: () => {
					startEditingShapeWithRichText(this.editor, this.shape.id)
				},
			})
		}
	}

	override onPointerUp() {
		this.complete()
	}

	override onInterrupt() {
		this.cancel()
	}

	override onComplete() {
		this.complete()
	}

	override onCancel() {
		this.cancel()
	}

	private complete() {
		if (this.editor.getInstanceState().isToolLocked) {
			this.parent.transition('idle')
		} else {
			startEditingShapeWithRichText(this.editor, this.shape.id, { info: this.info })
		}
	}

	private cancel() {
		this.editor.bailToMark(this.markId)
		this.parent.transition('idle', this.info)
	}
}

export function getNoteShapeAdjacentPositionOffset(editor: Editor, center: Vec, scale: number) {
	let min = NOTE_ADJACENT_POSITION_SNAP_RADIUS / editor.getZoomLevel() // in screen space
	let offset: Vec | undefined
	for (const pit of getAvailableNoteAdjacentPositions(editor, 0, scale, 0)) {
		// only check page rotations of zero
		const deltaToPit = Vec.Sub(center, pit)
		const dist = deltaToPit.len()
		if (dist < min) {
			min = dist
			offset = deltaToPit
		}
	}
	return offset
}

export function createNoteShape(editor: Editor, id: TLShapeId, center: Vec) {
	editor.createShape({
		id,
		type: 'note',
		x: center.x,
		y: center.y,
		props: {
			scale: editor.getResizeScaleFactor(),
		},
	})

	const shape = editor.getShape<TLNoteShape>(id)
	// Should never happen since we just checked, but just in case
	if (!shape) return

	editor.select(id)
	const bounds = editor.getShapeGeometry(shape).bounds
	const newPoint = maybeSnapToGrid(
		new Vec(shape.x - bounds.width / 2, shape.y - bounds.height / 2),
		editor
	)

	// Center the text around the created point
	editor.updateShapes([
		{
			id,
			type: 'note',
			x: newPoint.x,
			y: newPoint.y,
		},
	])

	return editor.getShape<TLNoteShape>(id)
}
