import { Prisma } from "@prisma/client"
import { prisma } from "./prisma-client"

export type AccountPayload = { userId: string; username: string } | { orgId: string }

/**
 * Get total optimization event count for a user.
 */
export async function getOptimizationEventCountByUserAccount(payload: AccountPayload) {
  const where =
    "orgId" in payload
      ? // for organization account
        {
          repository: {
            organization_id: payload.orgId,
          },
        }
      : // for personal account
        {
          OR: [{ user_id: payload.userId }, { current_username: payload.username }],
        }
  return prisma.optimization_events.count({
    where,
  })
}
export function buildOptimizationOrCondition(
  payload: AccountPayload,
  repoIds: string[],
): Prisma.optimization_eventsWhereInput {
  if ("orgId" in payload) {
    return {
      repository_id: { in: repoIds },
    }
  } else {
    return {
      AND: [
        {
          OR: [
            { repository_id: { in: repoIds } },
            { user_id: payload.userId },
            { current_username: payload.username },
          ],
        },
      ],
    }
  }
}
export async function getUserOptimizationSuccessfulCountForRepoIds(
  repoIds: string[],
  payload: AccountPayload,
) {
  return await prisma.optimization_events.count({
    where: { ...buildOptimizationOrCondition(payload, repoIds), is_optimization_found: true },
  })
}
export async function getOptimizationEventCountByRepoIds(
  repoIds: string[],
  payload: AccountPayload,
) {
  return await prisma.optimization_events.count({
    where: {
      ...buildOptimizationOrCondition(payload, repoIds),
    },
  })
}
/**
 * Get total optimization event count by repository and user.
 */
export async function getOptimizationEventCountByRepoAndUser(
  repoId: string,
  userId: string,
  username: string,
) {
  return await prisma.optimization_events.count({
    where: {
      repository_id: repoId,
      OR: [{ user_id: userId }, { current_username: username }],
    },
  })
}

const getWhereClauseForAccount = (payload: AccountPayload) => {
  return "orgId" in payload
    ? // for organization account
      {
        repository: {
          organization_id: payload.orgId,
        },
      }
    : // for personal account
      {
        AND: [
          {
            OR: [
              {
                repository: {
                  repository_members: {
                    some: {
                      user_id: payload.userId,
                    },
                  },
                },
              },
              {
                current_username: payload.username,
              },
              { user_id: payload.userId },
            ],
          },
        ],
      }
}

/**
 * Get count of distinct active repositories in the last 30 days.
 */
export async function getActiveRepoCountLast30Days(payload: AccountPayload) {
  const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)

  const activeRepos = await prisma.optimization_events.findMany({
    where: {
      created_at: {
        gte: since,
      },
      ...getWhereClauseForAccount(payload),
    },
    distinct: ["repository_id"],
    select: {
      repository_id: true,
    },
  })

  return activeRepos.length
}
/**
 *   count of distinct active repositories from a list of repository IDs in the last 30 days.
 * ***/
export async function getActiveRepoIdsLast30DaysByRepoIds(
  repoIds: string[],
  payload: AccountPayload,
) {
  const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)

  const activeRepos = await prisma.optimization_events.findMany({
    where: {
      created_at: {
        gte: since,
      },
      ...buildOptimizationOrCondition(payload, repoIds),
    },
    distinct: ["repository_id"],
    select: {
      repository_id: true,
    },
  })

  return activeRepos.length
}
export async function getOptimizationsTimeSeriesDataByRepoIds(
  repoIds: string[],
  payload: AccountPayload,
  onlySuccessful?: boolean,
) {
  const OR = buildOptimizationOrCondition(payload, repoIds)
  return await prisma.optimization_events.findMany({
    where: {
      ...buildOptimizationOrCondition(payload, repoIds),
      ...(onlySuccessful === true ? { is_optimization_found: true } : {}),
    },
    select: {
      created_at: true,
    },
  })
}
/**
 * Mark an optimization event as successful or not.
 */
export async function markOptimizationSuccess(trace_id: string, is_optimization_found: boolean) {
  return await prisma.optimization_events.update({
    where: {
      trace_id: trace_id,
    },
    data: {
      is_optimization_found: is_optimization_found,
    },
  })
}

/**
 * Update the event type of an optimization event.
 */
export async function updateOptimizationEventType(eventId: string, eventType: string) {
  return await prisma.optimization_events.update({
    where: {
      id: eventId,
    },
    data: {
      event_type: eventType,
    },
  })
}

export type GetOptimizationEventsPayload = {
  eventTypes?: string[]
  forAccount: AccountPayload
  year?: number
  select?: Prisma.optimization_eventsSelect
}

export async function getOptimizationEvents(payload: GetOptimizationEventsPayload) {
  const where = getWhereClauseForAccount(payload.forAccount)
  if (payload.eventTypes) {
    where["event_type"] = { in: payload.eventTypes }
  }
  if (payload.year) {
    where["created_at"] = {
      gte: new Date(`${payload.year}-01-01T00:00:00.000Z`),
      lt: new Date(`${payload.year + 1}-01-01T00:00:00.000Z`),
    }
  }
  const data = await prisma.optimization_events.findMany({
    where,
    select: payload.select,
  })

  return data
}

export async function getOptimizationEventsByRepoIds(
  repoIds: string[],
  payload: GetOptimizationEventsPayload,
) {
  const where: Prisma.optimization_eventsWhereInput = {
    ...buildOptimizationOrCondition(payload.forAccount, repoIds),
  }
  if (payload.eventTypes) {
    where["event_type"] = { in: payload.eventTypes }
  }
  if (payload.year) {
    where["created_at"] = {
      gte: new Date(`${payload.year}-01-01T00:00:00.000Z`),
      lt: new Date(`${payload.year + 1}-01-01T00:00:00.000Z`),
    }
  }
  const data = await prisma.optimization_events.findMany({
    where,
  })

  return data
}
