import { describe, it, expect, vi, beforeEach } from "vitest"
import { prisma } from "@codeflash-ai/common"
import { getRepositoriesForAccountCached } from "@/lib/services/repository-utils"
import { trackRepositoryConnected } from "@/lib/analytics/tracking"
import { getActionAccountContext } from "@/lib/server/get-account-context"

vi.mock("@/lib/server-action-timing", () => ({
  withTiming: vi.fn((_name: string, fn: Function) => fn),
}))

vi.mock("@/lib/services/repository-utils", () => ({
  getRepositoriesForAccountCached: vi.fn(),
}))

vi.mock("@/lib/analytics/tracking", () => ({
  trackMemberInvited: vi.fn(),
  trackRepositoryConnected: vi.fn(),
}))

vi.mock("@/lib/server/get-account-context", () => ({
  getActionAccountContext: vi.fn(),
}))

const mockRepo = {
  id: "repo-1",
  github_repo_id: "12345",
  name: "my-repo",
  full_name: "myorg/my-repo",
  is_private: false,
  has_github_action: true,
  created_at: new Date("2024-01-01"),
  last_optimized: new Date("2024-06-01"),
  optimizations_limit: 100,
  optimizations_used: 50,
  repository_members: [{ id: "rm-1" }, { id: "rm-2" }],
  // Matches the Prisma return shape for include: { _count: { select: { repository_members: true } } }
  _count: { repository_members: 2 },
}

const mockPayload = { userId: "user-1", username: "testuser" }

describe("getRepositoryById", () => {
  let getRepositoryById: typeof import("../queries").getRepositoryByIdForPayload

  beforeEach(async () => {
    vi.clearAllMocks()
    const mod = await import("../queries")
    getRepositoryById = mod.getRepositoryByIdForPayload
  })

  describe("parallel fetch", () => {
    it("fetches repo and authorized repoIds concurrently", async () => {
      vi.mocked(prisma.repositories.findUnique).mockResolvedValue(mockRepo as any)
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["repo-1"],
        repos: [],
      } as any)
      vi.mocked(prisma.optimization_events.count).mockResolvedValue(5)

      await getRepositoryById(mockPayload as any, "repo-1")

      expect(prisma.repositories.findUnique).toHaveBeenCalledTimes(1)
      expect(getRepositoriesForAccountCached).toHaveBeenCalledWith(mockPayload)
    })

    it("returns null when repo is not found", async () => {
      vi.mocked(prisma.repositories.findUnique).mockResolvedValue(null)
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["repo-1"],
        repos: [],
      } as any)

      const result = await getRepositoryById(mockPayload as any, "repo-1")
      expect(result).toBeNull()
    })

    it("returns null when repo is not in authorized list", async () => {
      vi.mocked(prisma.repositories.findUnique).mockResolvedValue(mockRepo as any)
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["other-repo"],
        repos: [],
      } as any)

      const result = await getRepositoryById(mockPayload as any, "repo-1")
      expect(result).toBeNull()
    })
  })

  describe("successful retrieval", () => {
    beforeEach(() => {
      vi.mocked(prisma.repositories.findUnique).mockResolvedValue(mockRepo as any)
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["repo-1"],
        repos: [],
      } as any)
    })

    it("returns RepositoryWithUsage with all required fields", async () => {
      vi.mocked(prisma.optimization_events.count).mockResolvedValue(3)

      const result = await getRepositoryById(mockPayload as any, "repo-1")

      expect(result).toEqual({
        id: "repo-1",
        github_repo_id: "12345",
        name: "my-repo",
        full_name: "myorg/my-repo",
        is_private: false,
        is_active: true,
        has_github_action: true,
        created_at: new Date("2024-01-01"),
        last_optimized: new Date("2024-06-01"),
        optimizations_limit: 100,
        optimizations_used: 50,
        organization: "myorg",
        avatarUrl: "https://github.com/myorg.png",
        membersCount: 2,
      })
    })

    it("sets is_active to false when no recent events", async () => {
      vi.mocked(prisma.optimization_events.count).mockResolvedValue(0)

      const result = await getRepositoryById(mockPayload as any, "repo-1")
      expect(result!.is_active).toBe(false)
    })

    it("sets is_active to true when recent events exist", async () => {
      vi.mocked(prisma.optimization_events.count).mockResolvedValue(10)

      const result = await getRepositoryById(mockPayload as any, "repo-1")
      expect(result!.is_active).toBe(true)
    })
  })

  describe("analytics tracking", () => {
    beforeEach(() => {
      vi.mocked(prisma.repositories.findUnique).mockResolvedValue(mockRepo as any)
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["repo-1"],
        repos: [],
      } as any)
      vi.mocked(prisma.optimization_events.count).mockResolvedValue(1)
    })

    it("calls trackRepositoryConnected for user payloads", async () => {
      await getRepositoryById(mockPayload as any, "repo-1")

      expect(trackRepositoryConnected).toHaveBeenCalledWith("user-1", {
        repositoryId: "repo-1",
        repositoryName: "myorg/my-repo",
      })
    })
  })

  describe("error handling", () => {
    it("returns null and logs when Prisma throws", async () => {
      vi.spyOn(console, "error").mockImplementation(() => {})
      vi.mocked(prisma.repositories.findUnique).mockRejectedValue(new Error("timeout"))
      vi.mocked(getRepositoriesForAccountCached).mockResolvedValue({
        repoIds: ["repo-1"],
        repos: [],
      } as any)

      const result = await getRepositoryById(mockPayload as any, "repo-1")
      expect(result).toBeNull()
    })
  })
})

describe("getRepositoryMembers", () => {
  let getRepositoryMembers: typeof import("../action").getRepositoryMembers

  beforeEach(async () => {
    vi.clearAllMocks()
    vi.mocked(getActionAccountContext).mockResolvedValue({
      payload: mockPayload,
      userId: "user-1",
      username: "testuser",
    })
    ;(prisma.repository_members as any).findMany = vi.fn()

    const mod = await import("../action")
    getRepositoryMembers = mod.getRepositoryMembers
  })

  it("uses the authenticated session user for the access check", async () => {
    vi.mocked(prisma.repository_members.findUnique).mockResolvedValue({ id: "member-1" } as any)
    vi.mocked(prisma.repository_members.findMany).mockResolvedValue([
      {
        id: "member-1",
        user_id: "user-1",
        role: "admin",
        added_at: new Date("2024-01-01"),
        user: { github_username: "alice" },
      },
    ] as any)

    const result = await getRepositoryMembers("repo-1")

    expect(result.success).toBe(true)
    expect(prisma.repository_members.findUnique).toHaveBeenCalledWith({
      where: { repository_id_user_id: { repository_id: "repo-1", user_id: "user-1" } },
      select: { id: true },
    })
  })

  it("returns unauthorized when there is no active session", async () => {
    vi.mocked(getActionAccountContext).mockResolvedValue(null)

    const result = await getRepositoryMembers("repo-1")

    expect(result.success).toBe(false)
    expect(result.error).toBe("Unauthorized")
    expect(prisma.repository_members.findUnique).not.toHaveBeenCalled()
  })
})
