import { Tokenizer } from "./tokenizer.js"
import { expect, test } from "bun:test"

test("parse 'fetch url' into command and positional tokens", () => {
  const { tokens, error } = Tokenizer.tokens("fetch https://example.com/path true")
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "https://example.com/path", start: 6, end: 30 },
    { type: "positional", value: "true", start: 31, end: 35 },
  ])
})

test("parse 'fetch \"quoted url\"' and strip quotes from value", () => {
  const { tokens, error } = Tokenizer.tokens('fetch "https://example.com/path"')
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "https://example.com/path", start: 6, end: 32 },
  ])
})

test("parse 'fetch url timeout=30' with named argument", () => {
  const { tokens, error } = Tokenizer.tokens("fetch https://example.com/path timeout=30")
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "https://example.com/path", start: 6, end: 30 },
    { type: "named", name: "timeout", value: "30", start: 31, end: 41 },
  ])
})

test("parse mixed args 'fetch url timeout=30 raw=true'", () => {
  const { tokens, error } = Tokenizer.tokens("fetch https://example.com/path timeout=30 raw=true")
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "https://example.com/path", start: 6, end: 30 },
    { type: "named", name: "timeout", value: "30", start: 31, end: 41 },
    { type: "named", name: "raw", value: "true", start: 42, end: 50 },
  ])
})

test("return error for unclosed quote in 'fetch \"unterminated'", () => {
  const { tokens, error } = Tokenizer.tokens('fetch "https://example.com/path')
  expect(tokens).toEqual([])
  expect(error).toBeDefined()
  expect(error?.message).toBe("Unclosed quote")
  expect(error?.start).toBe(6)
  expect(error?.end).toBe(31)
})

test("return error for escape char at end", () => {
  const { tokens, error } = Tokenizer.tokens("fetch https://example.com/path\\")
  expect(tokens).toEqual([])
  expect(error).toBeDefined()
  expect(error?.message).toBe("Trailing backslash")
  expect(error?.start).toBe(30)
  expect(error?.end).toBe(31)
})

test('handle escaped quotes in \'fetch "url with \\"quotes\\""\'', () => {
  const { tokens, error } = Tokenizer.tokens('fetch "url with \\"quotes\\""')
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: 'url with "quotes"', start: 6, end: 27 },
  ])
  expect(error).toBeUndefined()
})

test("handle escaped quotes in in named arg", () => {
  const { tokens, error } = Tokenizer.tokens('fetch url timeout="30 seconds" raw=true')
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "url", start: 6, end: 9 },
    { type: "named", name: "timeout", value: "30 seconds", start: 10, end: 30 },
    { type: "named", name: "raw", value: "true", start: 31, end: 39 },
  ])
  expect(error).toBeUndefined()
})

test("ignore multiple spaces between arguments", () => {
  const { tokens, error } = Tokenizer.tokens(
    "fetch    https://example.com/path   timeout=30   raw=true"
  )
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "https://example.com/path", start: 9, end: 33 },
    { type: "named", name: "timeout", value: "30", start: 36, end: 46 },
    { type: "named", name: "raw", value: "true", start: 49, end: 57 },
  ])
  expect(error).toBeUndefined()
})

test("return empty array for empty string input", () => {
  const { tokens, error } = Tokenizer.tokens("")
  expect(tokens).toEqual([])
  expect(error).toBeUndefined()
})

test("handle quote immediately followed by text 'fetch \"url\"more'", () => {
  const { tokens, error } = Tokenizer.tokens('fetch "url"more timeout=30')
  expect(tokens).toEqual([
    { type: "command", value: "fetch", start: 0, end: 5 },
    { type: "positional", value: "urlmore", start: 6, end: 15 },
    { type: "named", name: "timeout", value: "30", start: 16, end: 26 },
  ])
  expect(error).toBeUndefined()
})
