import { describe, expect, it, beforeEach, afterEach, spyOn } from "bun:test"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { writeFileSync, readFileSync, existsSync, rmSync, mkdirSync } from "node:fs"
import { parseJsonc } from "../../shared/jsonc-parser"
import type { InstallConfig } from "../types"
import { resetConfigContext } from "./config-context"

let testConfigPath: string
let testConfigDir: string
let testCounter = 0
let fetchVersionSpy: unknown

beforeEach(async () => {
  testCounter++
  testConfigDir = join(tmpdir(), `test-opencode-${Date.now()}-${testCounter}`)
  testConfigPath = join(testConfigDir, "opencode.jsonc")
  mkdirSync(testConfigDir, { recursive: true })

  process.env.OPENCODE_CONFIG_DIR = testConfigDir
  resetConfigContext()

  const module = await import("./auth-plugins")
  fetchVersionSpy = spyOn(module, "fetchLatestVersion").mockResolvedValue("1.2.3")
})

afterEach(() => {
  try {
    rmSync(testConfigDir, { recursive: true, force: true })
  } catch {}
})

const testConfig: InstallConfig = {
  hasClaude: false,
  isMax20: false,
  hasOpenAI: false,
  hasGemini: true,
  hasCopilot: false,
  hasOpencodeZen: false,
  hasZaiCodingPlan: false,
  hasKimiForCoding: false,
}

describe("addAuthPlugins", () => {
  describe("Test 1: JSONC with commented plugin line", () => {
    it("preserves comment, does NOT add antigravity plugin", async () => {
      const content = `{
  // "plugin": ["old-plugin"]
  "plugin": ["existing-plugin"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(result.configPath, "utf-8")
      expect(newContent).toContain('// "plugin": ["old-plugin"]')
      expect(newContent).toContain('existing-plugin')
      // antigravity plugin should NOT be auto-added anymore
      expect(newContent).not.toContain('opencode-antigravity-auth')

      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      const plugins = parsed.plugin as string[]
      expect(plugins).toContain('existing-plugin')
      expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false)
    })
  })

  describe("Test 2: Plugin array already contains antigravity", () => {
    it("preserves existing antigravity, does not add another", async () => {
      const content = `{
  "plugin": ["existing-plugin", "opencode-antigravity-auth"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(testConfigPath, "utf-8")
      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      const plugins = parsed.plugin as string[]

      const antigravityCount = plugins.filter((p) => p.startsWith('opencode-antigravity-auth')).length
      expect(antigravityCount).toBe(1)
      expect(plugins).toContain('existing-plugin')
    })
  })

  describe("Test 3: Backup created before write", () => {
    it("creates .bak file", async () => {
      const originalContent = `{
  "plugin": ["existing-plugin"],
  "provider": {}
}`
      writeFileSync(testConfigPath, originalContent, "utf-8")
      readFileSync(testConfigPath, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)
      expect(existsSync(`${result.configPath}.bak`)).toBe(true)

      const backupContent = readFileSync(`${result.configPath}.bak`, "utf-8")
      expect(backupContent).toBe(originalContent)
    })
  })

  describe("Test 4: Comment with } character", () => {
    it("preserves comments with special characters", async () => {
      const content = `{
  // This comment has } special characters
  "plugin": ["existing-plugin"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(testConfigPath, "utf-8")
      expect(newContent).toContain('// This comment has } special characters')

      expect(() => parseJsonc(newContent)).not.toThrow()
    })
  })

  describe("Test 5: Comment containing 'plugin' string", () => {
    it("must NOT match comment location", async () => {
      const content = `{
  // "plugin": ["fake"]
  "plugin": ["existing-plugin"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(testConfigPath, "utf-8")
      expect(newContent).toContain('// "plugin": ["fake"]')

      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      const plugins = parsed.plugin as string[]
      expect(plugins).toContain('existing-plugin')
      expect(plugins).not.toContain('fake')
    })
  })

  describe("Test 6: No existing plugin array", () => {
    it("creates empty plugin array when none exists, does NOT add antigravity", async () => {
      const content = `{
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(result.configPath, "utf-8")

      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      expect(parsed).toHaveProperty('plugin')
      const plugins = parsed.plugin as string[]
      // antigravity plugin should NOT be auto-added anymore
      expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false)
      expect(plugins.length).toBe(0)
    })
  })

  describe("Test 7: Post-write validation ensures valid JSONC", () => {
    it("result file must be valid JSONC", async () => {
      const content = `{
  "plugin": ["existing-plugin"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(testConfigPath, "utf-8")
      expect(() => parseJsonc(newContent)).not.toThrow()

      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      expect(parsed).toHaveProperty('plugin')
      expect(parsed).toHaveProperty('provider')
    })
  })

  describe("Test 8: Multiple plugins in array", () => {
    it("preserves existing plugins, does NOT add antigravity", async () => {
      const content = `{
  "plugin": ["plugin-1", "plugin-2", "plugin-3"],
  "provider": {}
}`
      writeFileSync(testConfigPath, content, "utf-8")

      const { addAuthPlugins } = await import("./auth-plugins")
      const result = await addAuthPlugins(testConfig)

      expect(result.success).toBe(true)

      const newContent = readFileSync(result.configPath, "utf-8")
      const parsed = parseJsonc<Record<string, unknown>>(newContent)
      const plugins = parsed.plugin as string[]

      expect(plugins).toContain('plugin-1')
      expect(plugins).toContain('plugin-2')
      expect(plugins).toContain('plugin-3')
      // antigravity plugin should NOT be auto-added anymore
      expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false)
      expect(plugins.length).toBe(3)
    })
  })
})
