import { createShapeId, TLDrawShape } from '@tldraw/editor'
import { TestEditor } from '../../../test/TestEditor'
import { createDrawSegments, pointsToBase64 } from '../../utils/test-helpers'

let editor: TestEditor

beforeEach(() => {
	editor = new TestEditor()
})

afterEach(() => {
	editor?.dispose()
})

describe('DrawShapeUtil dot detection', () => {
	const shapeId = createShapeId('test-draw')

	function createDrawShape(segments: TLDrawShape['props']['segments']): TLDrawShape {
		editor.createShapes([
			{
				id: shapeId,
				type: 'draw',
				props: { segments },
			},
		])
		return editor.getShape(shapeId) as TLDrawShape
	}

	describe('getIsDot behavior via hideResizeHandles', () => {
		it('treats a shape with one segment and zero points as a dot', () => {
			const shape = createDrawShape([{ type: 'free', path: '' }])
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(true)
		})

		it('treats a shape with one segment and one point as a dot', () => {
			const shape = createDrawShape(createDrawSegments([[{ x: 0, y: 0, z: 0.5 }]]))
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(true)
		})

		it('treats a shape with one segment and two points as NOT a dot', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 10, y: 10, z: 0.5 },
					],
				])
			)
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(false)
		})

		it('treats a shape with one segment and many points as NOT a dot', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 10, y: 10, z: 0.5 },
						{ x: 20, y: 5, z: 0.5 },
						{ x: 30, y: 15, z: 0.5 },
					],
				])
			)
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(false)
		})

		it('treats a shape with multiple segments as NOT a dot', () => {
			const shape = createDrawShape(
				createDrawSegments([[{ x: 0, y: 0, z: 0.5 }], [{ x: 10, y: 10, z: 0.5 }]])
			)
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(false)
		})
	})

	describe('hideRotateHandle mirrors hideResizeHandles for dots', () => {
		it('hides rotate handle for dots', () => {
			const shape = createDrawShape(createDrawSegments([[{ x: 0, y: 0, z: 0.5 }]]))
			const util = editor.getShapeUtil('draw')
			expect(util.hideRotateHandle(shape)).toBe(true)
		})

		it('shows rotate handle for non-dots', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 10, y: 10, z: 0.5 },
					],
				])
			)
			const util = editor.getShapeUtil('draw')
			expect(util.hideRotateHandle(shape)).toBe(false)
		})
	})

	describe('hideSelectionBoundsFg mirrors hideResizeHandles for dots', () => {
		it('hides selection bounds for dots', () => {
			const shape = createDrawShape(createDrawSegments([[{ x: 0, y: 0, z: 0.5 }]]))
			const util = editor.getShapeUtil('draw')
			expect(util.hideSelectionBoundsFg(shape)).toBe(true)
		})

		it('shows selection bounds for non-dots', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 10, y: 10, z: 0.5 },
					],
				])
			)
			const util = editor.getShapeUtil('draw')
			expect(util.hideSelectionBoundsFg(shape)).toBe(false)
		})
	})

	describe('onResize prevents zero scale', () => {
		it('is a no-op when resized to zero width', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 50, y: 50, z: 0.5 },
					],
				])
			)

			editor.resizeShape(shape.id, { x: 0, y: 1 })
			const resized = editor.getShape<TLDrawShape>(shapeId)!
			expect(resized.props.scaleX).toBe(1)
			expect(resized.props.scaleY).toBe(1)
		})

		it('is a no-op when resized to zero height', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 50, y: 50, z: 0.5 },
					],
				])
			)

			editor.resizeShape(shape.id, { x: 1, y: 0 })
			const resized = editor.getShape<TLDrawShape>(shapeId)!
			expect(resized.props.scaleX).toBe(1)
			expect(resized.props.scaleY).toBe(1)
		})

		it('preserves sign when resizing with negative scale (flipping)', () => {
			const shape = createDrawShape(
				createDrawSegments([
					[
						{ x: 0, y: 0, z: 0.5 },
						{ x: 50, y: 50, z: 0.5 },
					],
				])
			)

			editor.resizeShape(shape.id, { x: -1, y: -1 })
			const resized = editor.getShape<TLDrawShape>(shapeId)!
			expect(resized.props.scaleX).toBe(-1)
			expect(resized.props.scaleY).toBe(-1)
		})
	})

	describe('base64 encoding boundary conditions', () => {
		it('correctly handles the boundary at exactly 24 base64 characters (2 points)', () => {
			// First point is 16 base64 chars (Float32), delta points are 8 chars each (Float16)
			// 2 points = 24 characters, which should NOT be a dot
			const twoPointsBase64 = pointsToBase64([
				{ x: 0, y: 0, z: 0.5 },
				{ x: 1, y: 1, z: 0.5 },
			])
			expect(twoPointsBase64.length).toBe(24)

			const shape = createDrawShape([{ type: 'free', path: twoPointsBase64 }])
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(false)
		})

		it('correctly handles the boundary at exactly 16 base64 characters (1 point)', () => {
			// 1 point = 16 characters (Float32 first point), which should be a dot
			const onePointBase64 = pointsToBase64([{ x: 0, y: 0, z: 0.5 }])
			expect(onePointBase64.length).toBe(16)

			const shape = createDrawShape([{ type: 'free', path: onePointBase64 }])
			const util = editor.getShapeUtil('draw')
			expect(util.hideResizeHandles(shape)).toBe(true)
		})
	})
})
