"use server"

import * as Sentry from "@sentry/node"
import { AccountPayload, createOrUpdateUser, getUserById, prisma } from "@codeflash-ai/common"
import { eachDayOfInterval, startOfDay } from "date-fns"
import { GitHubUserSearchResult, Member, UserRole } from "@/lib/types"
import { ActionResponse, createErrorResponse, createSuccessResponse } from "@/lib/action-response"
import { RepositoryWithUsage } from "@/app/dashboard/action"
import { getRepositoriesForAccountCached } from "@/lib/services/repository-utils"

export async function getOptimizationsTimeSeriesData(repoId: string, onlySuccessful?: boolean) {
  try {
    const data = await prisma.optimization_events.findMany({
      where: {
        ...(onlySuccessful === true ? { is_optimization_found: true } : {}),
        repository_id: repoId,
      },
      select: {
        created_at: true,
      },
    })

    const groupedByDay: Record<string, number> = {}

    data.forEach(item => {
      const day = item.created_at
        .toLocaleDateString(undefined, {
          timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
          year: "numeric",
          month: "2-digit",
          day: "2-digit",
        })
        .replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2")
      groupedByDay[day] = (groupedByDay[day] || 0) + 1
    })

    const allDates = eachDayOfInterval({
      start: new Date(Object.keys(groupedByDay).sort()[0]),
      end: startOfDay(new Date()),
    }).map(d =>
      d
        .toLocaleDateString(undefined, {
          timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
          year: "numeric",
          month: "2-digit",
          day: "2-digit",
        })
        .replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2"),
    )

    let cumulativeCount = 0
    const completeData = allDates.map(date => {
      cumulativeCount += groupedByDay[date] || 0
      return { date, count: cumulativeCount }
    })

    return completeData
  } catch (error) {
    console.error("Failed to fetch optimization time series data:", error)
    return []
  }
}

export async function getPullRequestEventTimeSeriesData(year: number, repoId: string) {
  try {
    const eventTypes = ["pr_created", "pr_merged", "pr_closed"]
    const data = await prisma.optimization_events.findMany({
      where: {
        event_type: { in: eventTypes },
        created_at: {
          gte: new Date(`${year}-01-01T00:00:00.000Z`),
          lt: new Date(`${year + 1}-01-01T00:00:00.000Z`),
        },
        repository_id: repoId,
      },
      select: {
        event_type: true,
        created_at: true,
      },
    })

    const groupedByMonth: Record<string, Record<string, number>> = {}

    for (let month = 1; month <= 12; month++) {
      const monthKey = `${year}-${month.toString().padStart(2, "0")}`
      groupedByMonth[monthKey] = { pr_created: 0, pr_merged: 0, pr_closed: 0 }
    }

    data.forEach(item => {
      const month = item.created_at.getMonth() + 1
      const monthKey = `${year}-${month.toString().padStart(2, "0")}`
      if (groupedByMonth[monthKey]) {
        groupedByMonth[monthKey][item.event_type] += 1
      }
    })

    const completeData = Object.keys(groupedByMonth).map(monthKey => ({
      month: monthKey,
      pr_created: groupedByMonth[monthKey].pr_created,
      pr_merged: groupedByMonth[monthKey].pr_merged,
      pr_closed: groupedByMonth[monthKey].pr_closed,
    }))

    return completeData
  } catch (error) {
    console.error("Failed to fetch pull request event time series data:", error)
    return []
  }
}

export async function getUserOptimizationCountByRepo(repoId: string) {
  return prisma.optimization_events.count({
    where: {
      repository_id: repoId,
    },
  })
}

export async function getUserOptimizationSuccessfulCountByRepo(repoId: string) {
  return prisma.optimization_events.count({
    where: {
      is_optimization_found: true,
      repository_id: repoId,
    },
  })
}

export async function getActiveUserLeaderboardLast30DaysForRepo(
  repoId: string,
): Promise<{ username: string; eventCount: number; avatarUrl: string }[]> {
  const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)

  const groupedCounts = await prisma.optimization_events.groupBy({
    by: ["current_username"],
    where: {
      repository_id: repoId,
      created_at: {
        gte: since,
      },
      current_username: {
        not: null,
      },
    },
    _count: {
      id: true,
    },
    orderBy: {
      _count: {
        id: "desc",
      },
    },
  })

  return groupedCounts.map(entry => ({
    username: entry.current_username!,
    eventCount: entry._count.id,
    avatarUrl: `https://github.com/${entry.current_username}.png`,
  }))
}

export async function getRepositoryById(
  payload: AccountPayload,
  repoId: string,
): Promise<RepositoryWithUsage | null> {
  try {
    const repo = await prisma.repositories.findFirst({
      where: {
        id: repoId,
      },
      include: {
        repository_members: true,
      },
    })
    const repoIds = await (await getRepositoriesForAccountCached(payload)).repoIds

    if (!repo || !repoIds.includes(repo.id)) return null

    const recentEventCount = await prisma.optimization_events.count({
      where: {
        repository_id: repo.id,
        created_at: {
          gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
        },
      },
    })

    return {
      id: repo.id,
      github_repo_id: repo.github_repo_id,
      name: repo.name,
      full_name: repo.full_name,
      is_private: repo.is_private,
      is_active: recentEventCount > 0,
      has_github_action: repo.has_github_action,
      created_at: repo.created_at,
      last_optimized: repo.last_optimized,
      optimizations_limit: repo.optimizations_limit,
      optimizations_used: repo.optimizations_used,
      organization: repo.full_name.split("/")[0],
      avatarUrl: `https://github.com/${repo.full_name.split("/")[0]}.png`,
      membersCount: repo.repository_members.length,
    }
  } catch (error) {
    console.error("Failed to fetch repository by ID:", error)
    return null
  }
}

export async function addRepositoryMemberById(
  currentUserId: string,
  repoId: string,
  invitedUser: GitHubUserSearchResult,
  role: UserRole,
): Promise<ActionResponse> {
  try {
    const invitedUserId = `github|${invitedUser.githubUserId.toString()}`
    // Check if current user is admin or the only member
    const repo = await prisma.repositories.findFirst({
      where: { id: repoId },
      include: {
        repository_members: {
          include: {
            user: true,
          },
        },
      },
    })

    if (!repo) {
      return createErrorResponse("Repository not found")
    }

    const currentUserMember = repo.repository_members.find(m => m.user_id === currentUserId)

    // Check if user has permission to add members
    const isAdmin = currentUserMember?.role === "admin" || currentUserMember?.role === "owner"
    const isOnlyMember = repo.repository_members.length === 1 && currentUserMember // if only member we need to let him add because we are was not manage well the member role

    if (!isAdmin && !isOnlyMember) {
      return createErrorResponse("You don't have permission to add members")
    }

    // Check if member already exists by username
    const existingMember = repo.repository_members.find(m => m.user.user_id === invitedUserId)

    if (existingMember) {
      return createErrorResponse("User is already a member of this repository")
    }

    // Check if user exists in our database by github_username
    let user = await getUserById(invitedUserId)

    // If user doesn't exist, create them with their GitHub User ID
    if (!user) {
      user = await createOrUpdateUser(invitedUserId, invitedUser.username, null, null)
    }

    // Add user to repository members
    const newMember = await prisma.repository_members.create({
      data: {
        repository_id: repoId,
        user_id: user.user_id,
        role,
        added_by: currentUserId,
      },
    })

    return createSuccessResponse({
      id: newMember.id,
      user_id: newMember.user_id,
      username: invitedUser.username,
      role: newMember.role,
      added_at: newMember.added_at,
      avatarUrl: invitedUser.avatarUrl,
    })
  } catch (error) {
    //Log the error for debugging purposes sentry
    Sentry.captureException("Add Repository Member failed:" + error)

    return createErrorResponse("Failed to add repository member")
  }
}

/**
 * Get repository members
 */
export async function getRepositoryMembers(
  currentUserId: string,
  repoId: string,
): Promise<ActionResponse<Member[]>> {
  try {
    const repo = await prisma.repositories.findFirst({
      where: { id: repoId },
      include: {
        repository_members: {
          include: {
            user: true,
          },
        },
      },
    })

    if (!repo) {
      return createErrorResponse("Repository not found")
    }

    // Check if user has access
    const hasAccess = repo.repository_members.some(m => m.user_id === currentUserId)

    if (!hasAccess) {
      return createErrorResponse("You don't have access to this repository")
    }

    const members: Member[] = repo.repository_members.map(member => ({
      id: member.id,
      user_id: member.user_id,
      username: member.user.github_username,
      role: member.role,
      added_at: member.added_at,
      avatarUrl: `https://github.com/${member.user.github_username}.png`,
    }))

    return createSuccessResponse(members)
  } catch (error) {
    console.error("Failed to get repository members:", error)
    Sentry.captureException("Failed to get repository members:" + error)
    return createErrorResponse(error instanceof Error ? error.message : "Failed to get members")
  }
}

/**
 * Update repository member role
 */
export async function updateRepositoryMemberRole(
  currentUserId: string,
  repoId: string,
  memberId: string,
  newRole: UserRole,
): Promise<ActionResponse<Boolean>> {
  try {
    const repo = await prisma.repositories.findFirst({
      where: { id: repoId },
      include: {
        repository_members: true,
      },
    })

    if (!repo) {
      return createErrorResponse("Repository not found")
    }

    const currentUserMember = repo.repository_members.find(m => m.user_id === currentUserId)

    // Only admins and owners can change roles
    if (currentUserMember?.role !== "admin" && currentUserMember?.role !== "owner") {
      return createErrorResponse("Only admins can change member roles")
    }

    // Don't allow changing owner role
    const targetMember = repo.repository_members.find(m => m.id === memberId)
    if (targetMember?.role === "owner") {
      return createErrorResponse("Cannot change owner role")
    }

    if (targetMember?.user_id === currentUserId) {
      return createErrorResponse("Cannot change your own role")
    }

    await prisma.repository_members.update({
      where: { id: memberId },
      data: { role: newRole },
    })

    return createSuccessResponse(true)
  } catch (error) {
    console.error("Failed to update member role:", error)
    Sentry.captureException("Failed to update member role:" + error)
    return createErrorResponse(error instanceof Error ? error.message : "Failed to update role")
  }
}

/**
 * Remove repository member
 */
export async function removeRepositoryMember(
  currentUserId: string,
  repoId: string,
  memberId: string,
): Promise<ActionResponse<Boolean>> {
  try {
    const repo = await prisma.repositories.findFirst({
      where: { id: repoId },
      include: {
        repository_members: true,
      },
    })

    if (!repo) {
      return createErrorResponse("Repository not found")
    }

    const currentUserMember = repo.repository_members.find(m => m.user_id === currentUserId)
    const targetMember = repo.repository_members.find(m => m.id === memberId)

    if (!targetMember) {
      return createErrorResponse("Member not found")
    }

    // Cannot remove owner
    if (targetMember.role === "owner") {
      return createErrorResponse("Cannot remove repository owner")
    }

    // Check permissions
    const isAdmin = currentUserMember?.role === "admin" || currentUserMember?.role === "owner"
    const isSelf = targetMember.user_id === currentUserId

    if (!isAdmin && !isSelf) {
      return createErrorResponse("You don't have permission to remove this member")
    }

    if (isSelf) {
      return createErrorResponse("Cannot remove yourself")
    }

    await prisma.repository_members.delete({
      where: { id: memberId },
    })

    return createSuccessResponse(true)
  } catch (error) {
    console.error("Failed to remove repository member:", error)
    Sentry.captureException("Failed to remove repository member:" + error)
    return createErrorResponse(error instanceof Error ? error.message : "Failed to remove member")
  }
}
