import { compose, type ComposedRouteState } from "./routeParser"
import { $store } from "@racletteCore"
import { validate } from "uuid"
import type { Composition } from "@shared/types/core/Composition.types"
import type { InteractionLink } from "@shared/types/core/InteractionLink.types"

export type WidgetLayoutType = "default" | "spaced" | "card" | "border" | "full"

export interface CompositionSlot {
  slotName: string
  slotValue: string
  slotParams?: Record<string, string>
  slotLayout?: WidgetLayoutType
}

// TODO: transform this to an array
export const DEFAULT_SLOT_NAME = "page"
export const DEFAULT_MODAL_SLOT_NAME = "modal"
export const DEFAULT_INLINE_SLOT_NAME = "inline"

/**
 * Reserved composition name for work sessions
 * When detected, triggers special handling for shared sessions
 */
export const WORK_SESSION_COMPOSITION_NAME = "workSession"

/**
 * Extract sessionId from workSession composition route
 * Handles URLs like /workSession/id={uuid}
 * Returns the UUID if found, null otherwise
 */
const extractWorkSessionId = (route: string): string | null => {
  // Parse the route to check if it contains workSession composition
  const composed = compose(route)

  // Check if any composition in the route is "workSession"
  for (const [composition, paramsArray] of composed.map) {
    if (composition === WORK_SESSION_COMPOSITION_NAME) {
      // Extract id parameter from the first params object
      const params =
        Array.isArray(paramsArray) && paramsArray.length > 0
          ? paramsArray[0]
          : {}

      if (typeof params === "object" && params !== null && "id" in params) {
        const sessionId = params.id as string
        // Validate UUID format
        if (validate(sessionId)) {
          return sessionId
        }
      }
    }
  }

  return null
}

export const parseRouteToCompositionSlots = (
  route: string,
  defaultSlotName = DEFAULT_SLOT_NAME,
  addDefaultSlotIfMissing = false,
) => {
  // If it's a workSession route, we'll handle it specially
  // For now, parse normally - the composition will be updated after socket connection
  const composed = compose(route)
  return parseComposedToCompositionSlots(
    composed,
    defaultSlotName,
    addDefaultSlotIfMissing,
  )
}
// Fix number and boolean params
const coerceParams = (
  params: Record<string, string>,
): Record<string, string | number | boolean> =>
  Object.fromEntries(
    Object.entries(params).map(([key, value]) => {
      if (value === "true") return [key, true]
      if (value === "false") return [key, false]
      if (value !== "" && !isNaN(Number(value))) return [key, Number(value)]
      return [key, value]
    }),
  )
/**
 * Check if a route contains a workSession composition
 * Returns the sessionId if found, null otherwise
 */
export const getWorkSessionIdFromRoute = (route: string): string | null => {
  return extractWorkSessionId(route)
}

export const parseComposedToCompositionSlots = (
  composed: ComposedRouteState,
  defaultSlotName = DEFAULT_SLOT_NAME,
  addDefaultSlotIfMissing = false,
): CompositionSlot[] => {
  const composition = composed.map.map(
    ([slotValue, paramsArray, ...slots]) => ({
      slotName: (slots[0] as string) || defaultSlotName,
      slotValue: slotValue as string,
      slotParams: coerceParams(
        (paramsArray as Record<string, string>[])[0] || {},
      ),
    }),
  )

  // add landing page into the compositionSlots, if there is no defaultSlot set
  const landingPage = $store.getState(["ui", "landingPage"])

  if (
    !composition.some((slot) => slot.slotName === DEFAULT_SLOT_NAME) &&
    addDefaultSlotIfMissing &&
    landingPage
  ) {
    composition.push({
      slotName: DEFAULT_SLOT_NAME,
      slotValue: landingPage,
      slotParams: {},
    })
  }

  return composition
}

// TODO: care about the name
export const transformToOriginalState = (
  compositionSlots: CompositionSlot[],
  defaultSlotName = DEFAULT_SLOT_NAME,
): ComposedRouteState => {
  const map = compositionSlots.map(({ slotName, slotValue, slotParams }) => {
    const resolvedSlotName = slotName === defaultSlotName ? "" : slotName

    const paramsArray = slotParams ? [slotParams] : []

    return [slotValue, paramsArray, resolvedSlotName]
  })

  // TODO: care about anchor
  return { map, anchor: null }
}
export const findInteractionLinkByPathname = (
  pathname: string,
): InteractionLink | null => {
  const interactionLinks = $store.getState("interactionLinks")
  const composition =
    $store.selectors.compositions.getCompositionByPath(pathname)

  if (!composition) {
    return null
  }

  // Find interactionLink that references this composition
  const found = Object.values(interactionLinks).find(
    (link: InteractionLink) => link.composition === composition._id,
  )

  return found || null
}
/**
 * Get active compositions from current route
 */
export const getActiveCompositionsFromRoute = (
  fullPath: string,
): Composition[] => {
  const compositionSlots = parseRouteToCompositionSlots(
    fullPath,
    undefined,
    true,
  )
  const compositions: Composition[] = []

  compositionSlots.forEach((slot) => {
    const composition = $store.selectors.compositions.getCompositionByPath(
      slot.slotValue,
    )
    if (composition) {
      compositions.push(composition)
    }
  })

  return compositions
}
