import { describe, expect, test, beforeEach } from "bun:test"
import path from "path"
import { WriteTool } from "../../src/tool/write"
import { Instance } from "../../src/project/instance"
import { tmpdir } from "../fixture/fixture"
import { PermissionNext } from "../../src/permission/next"
import { Agent } from "../../src/agent/agent"
import { TodoWriteTool, TodoReadTool } from "../../src/tool/todo"
import { Todo } from "../../src/session/todo"

const ctx = {
  sessionID: "test",
  messageID: "",
  callID: "",
  agent: "build",
  abort: AbortSignal.any([]),
  messages: [],
  metadata: () => {},
  ask: async () => {},
}

describe("tool.write", () => {
  test("creates new file", async () => {
    await using tmp = await tmpdir({ git: true })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const write = await WriteTool.init()
        const result = await write.execute(
          { filePath: path.join(tmp.path, "new.txt"), content: "hello world" },
          ctx
        )
        expect(result.output).toContain("Wrote file successfully")
        
        const file = Bun.file(path.join(tmp.path, "new.txt"))
        expect(await file.exists()).toBe(true)
        expect(await file.text()).toBe("hello world")
      },
    })
  })

  test("overwrites existing file", async () => {
    await using tmp = await tmpdir({
      init: async (dir) => {
        await Bun.write(path.join(dir, "existing.txt"), "old content")
      },
    })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const write = await WriteTool.init()
        const result = await write.execute(
          { filePath: path.join(tmp.path, "existing.txt"), content: "new content" },
          ctx
        )
        expect(result.output).toContain("Wrote file successfully")
        
        const file = Bun.file(path.join(tmp.path, "existing.txt"))
        expect(await file.text()).toBe("new content")
      },
    })
  })

  test("handles nested directories", async () => {
    await using tmp = await tmpdir({ git: true })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const write = await WriteTool.init()
        const nestedPath = path.join(tmp.path, "deeply", "nested", "dir", "file.txt")
        await write.execute(
          { filePath: nestedPath, content: "nested content" },
          ctx
        )
        
        const file = Bun.file(nestedPath)
        expect(await file.exists()).toBe(true)
        expect(await file.text()).toBe("nested content")
      },
    })
  })

  test("sets metadata correctly", async () => {
    await using tmp = await tmpdir({
      init: async (dir) => {
        await Bun.write(path.join(dir, "existing.txt"), "old")
      },
    })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const write = await WriteTool.init()
        const result = await write.execute(
          { filePath: path.join(tmp.path, "existing.txt"), content: "new" },
          ctx
        )
        expect(result.metadata.exists).toBe(true)
        expect(result.metadata.filepath).toBe(path.join(tmp.path, "existing.txt"))
      },
    })
  })

  test("handles large files", async () => {
    await using tmp = await tmpdir({ git: true })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const largeContent = "x".repeat(100000)
        const write = await WriteTool.init()
        const result = await write.execute(
          { filePath: path.join(tmp.path, "large.txt"), content: largeContent },
          ctx
        )
        expect(result.output).toContain("Wrote file successfully")
        
        const file = Bun.file(path.join(tmp.path, "large.txt"))
        expect(await file.text()).toBe(largeContent)
      },
    })
  })

  test("writes JSON content", async () => {
    await using tmp = await tmpdir({ git: true })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const jsonContent = JSON.stringify({ key: "value", nested: { a: 1 } }, null, 2)
        const write = await WriteTool.init()
        await write.execute(
          { filePath: path.join(tmp.path, "data.json"), content: jsonContent },
          ctx
        )
        
        const file = Bun.file(path.join(tmp.path, "data.json"))
        const parsed = JSON.parse(await file.text())
        expect(parsed.key).toBe("value")
        expect(parsed.nested.a).toBe(1)
      },
    })
  })

  test("writes TypeScript content", async () => {
    await using tmp = await tmpdir({ git: true })
    await Instance.provide({
      directory: tmp.path,
      fn: async () => {
        const tsContent = `export function greet(name: string): string {\n  return \`Hello, \${name}!\`\n}`
        const write = await WriteTool.init()
        await write.execute(
          { filePath: path.join(tmp.path, "greet.ts"), content: tsContent },
          ctx
        )
        
        const file = Bun.file(path.join(tmp.path, "greet.ts"))
        expect(await file.text()).toBe(tsContent)
      },
    })
  })
})

describe("tool.todowrite", () => {
  beforeEach(async () => {
    await Todo.update({ sessionID: "test", todos: [] })
  })

  test("creates todo list", async () => {
    const todos = [
      { content: "Task 1", status: "pending", priority: "high" },
      { content: "Task 2", status: "pending", priority: "medium" },
    ]
    
    const result = await TodoWriteTool.execute({ todos }, ctx)
    
    expect(result.output).toContain("Task 1")
    expect(result.output).toContain("Task 2")
    expect(result.title).toBe("2 todos")
  })

  test("updates existing todos", async () => {
    const initialTodos = [
      { content: "Task 1", status: "pending", priority: "high" },
    ]
    await TodoWriteTool.execute({ todos: initialTodos }, ctx)
    
    const updatedTodos = [
      { content: "Task 1", status: "completed", priority: "high" },
      { content: "Task 2", status: "pending", priority: "medium" },
    ]
    const result = await TodoWriteTool.execute({ todos: updatedTodos }, ctx)
    
    expect(result.title).toBe("1 todos")
  })

  test("handles empty todo list", async () => {
    const result = await TodoWriteTool.execute({ todos: [] }, ctx)
    
    expect(result.title).toBe("0 todos")
    expect(result.output).toBe("[]")
  })

  test("handles all priority levels", async () => {
    const todos = [
      { content: "High priority", status: "pending", priority: "high" },
      { content: "Medium priority", status: "pending", priority: "medium" },
      { content: "Low priority", status: "pending", priority: "low" },
    ]
    
    const result = await TodoWriteTool.execute({ todos }, ctx)
    expect(result.metadata.todos.length).toBe(3)
  })

  test("handles all status values", async () => {
    const todos = [
      { content: "Pending task", status: "pending", priority: "high" },
      { content: "In progress task", status: "in_progress", priority: "high" },
      { content: "Completed task", status: "completed", priority: "high" },
      { content: "Cancelled task", status: "cancelled", priority: "high" },
    ]
    
    const result = await TodoWriteTool.execute({ todos }, ctx)
    expect(result.metadata.todos.length).toBe(4)
    expect(result.title).toBe("3 todos")
  })
})

describe("tool.todoread", () => {
  beforeEach(async () => {
    await Todo.update({ sessionID: "test", todos: [] })
  })

  test("reads empty todo list", async () => {
    const result = await TodoReadTool.execute({}, ctx)
    
    expect(result.output).toBe("[]")
    expect(result.title).toBe("0 todos")
  })

  test("reads existing todos", async () => {
    const todos = [
      { content: "Task 1", status: "pending", priority: "high" },
      { content: "Task 2", status: "completed", priority: "medium" },
    ]
    await TodoWriteTool.execute({ todos }, ctx)
    
    const result = await TodoReadTool.execute({}, ctx)
    const parsed = JSON.parse(result.output)
    
    expect(parsed.length).toBe(2)
    expect(parsed[0].content).toBe("Task 1")
    expect(parsed[1].status).toBe("completed")
  })

  test("counts non-completed todos in title", async () => {
    const todos = [
      { content: "Done", status: "completed", priority: "high" },
      { content: "Done 2", status: "completed", priority: "high" },
      { content: "Active", status: "pending", priority: "high" },
    ]
    await TodoWriteTool.execute({ todos }, ctx)
    
    const result = await TodoReadTool.execute({}, ctx)
    expect(result.title).toBe("1 todos")
  })
})
