import { describe, expect, test } from "bun:test"
import * as State from "@/saeeol/cost-tracker/state"
import { formatCost, formatTotalCost } from "@/saeeol/cost-tracker/format"

describe("CostTracker reset isolation", () => {
  test("reset clears all accumulated state", () => {
    State.addSessionCost(5, { input_tokens: 100, output_tokens: 50 }, "model-x")
    State.addLines(10, 5)
    State.addAPIDuration(1000, 800)
    State.addToolDuration(500)
    State.reset()
    expect(State.getTotalCost()).toBe(0)
    expect(State.getLines()).toEqual({ added: 0, removed: 0 })
    expect(State.getAPIDuration()).toBe(0)
    expect(State.getDuration()).toBe(0)
    expect(Object.keys(State.getModelUsage()).length).toBe(0)
  })
})

describe("CostTracker addAPIDuration", () => {
  test("accumulates API durations", () => {
    State.reset()
    State.addAPIDuration(500, 400)
    State.addAPIDuration(300, 250)
    expect(State.getAPIDuration()).toBe(800)
  })
})

describe("CostTracker addToolDuration", () => {
  test("accumulates tool durations", () => {
    State.reset()
    State.addToolDuration(100)
    State.addToolDuration(200)
    // tool duration is tracked internally, verified via formatTotalCost
    const report = formatTotalCost()
    expect(report).toContain("Total duration")
  })
})

describe("CostTracker getUsageForModel", () => {
  test("returns undefined for unknown model", () => {
    State.reset()
    expect(State.getUsageForModel("nonexistent")).toBeUndefined()
  })

  test("returns usage for known model", () => {
    State.reset()
    State.addSessionCost(0.1, { input_tokens: 500, output_tokens: 200 }, "known-model")
    const usage = State.getUsageForModel("known-model")
    expect(usage).toBeDefined()
    expect(usage!.inputTokens).toBe(500)
    expect(usage!.outputTokens).toBe(200)
    expect(usage!.costUSD).toBe(0.1)
  })
})

describe("CostTracker edge cases", () => {
  test("zero cost does not break accumulation", () => {
    State.reset()
    const returned = State.addSessionCost(0, { input_tokens: 0, output_tokens: 0 }, "free-model")
    expect(returned).toBe(0)
    expect(State.getTotalCost()).toBe(0)
  })

  test("negative lines are handled", () => {
    State.reset()
    State.addLines(-5, -2)
    // internal state tracks raw numbers; no clamping
    expect(State.getLines()).toEqual({ added: -5, removed: -2 })
  })

  test("web search requests counted", () => {
    State.reset()
    State.addSessionCost(0.01, {
      input_tokens: 100,
      output_tokens: 50,
      server_tool_use: { web_search_requests: 3 },
    }, "search-model")
    const usage = State.getUsageForModel("search-model")
    expect(usage!.webSearchRequests).toBe(3)
  })

  test("cache tokens accumulated", () => {
    State.reset()
    State.addSessionCost(0.01, {
      input_tokens: 100,
      output_tokens: 50,
      cache_read_input_tokens: 80,
      cache_creation_input_tokens: 20,
    }, "cache-model")
    const usage = State.getUsageForModel("cache-model")
    expect(usage!.cacheReadInputTokens).toBe(80)
    expect(usage!.cacheCreationInputTokens).toBe(20)
  })

  test("same model accumulated across calls", () => {
    State.reset()
    State.addSessionCost(0.01, { input_tokens: 100, output_tokens: 50 }, "model-a")
    State.addSessionCost(0.02, { input_tokens: 200, output_tokens: 100 }, "model-a")
    const usage = State.getUsageForModel("model-a")
    expect(usage!.inputTokens).toBe(300)
    expect(usage!.outputTokens).toBe(150)
    expect(usage!.costUSD).toBeCloseTo(0.03)
    expect(State.getTotalCost()).toBeCloseTo(0.03)
  })
})

describe("formatCost edge cases", () => {
  test("zero cost", () => {
    expect(formatCost(0)).toBe("$0.0000")
  })

  test("boundary at 0.5", () => {
    // 0.5 is not > 0.5, so uses 4 decimals
    expect(formatCost(0.5)).toBe("$0.5000")
  })

  test("just above boundary", () => {
    expect(formatCost(0.5001)).toBe("$0.50")
  })

  test("just below boundary", () => {
    expect(formatCost(0.4999)).toBe("$0.4999")
  })

  test("very small cost", () => {
    expect(formatCost(0.0001)).toBe("$0.0001")
  })
})

describe("formatTotalCost with multiple models", () => {
  test("includes all models in report", () => {
    State.reset()
    State.addSessionCost(0.1, { input_tokens: 1000, output_tokens: 500 }, "model-alpha")
    State.addSessionCost(0.2, { input_tokens: 2000, output_tokens: 1000 }, "model-beta")
    const report = formatTotalCost()
    expect(report).toContain("model-alpha")
    expect(report).toContain("model-beta")
    expect(report).toContain("$0.30")
  })
})
