import { z, ZodError, ZodIssueCode } from "zod"

import { Lang } from "@/constants/languages"
import { discriminatedUnion } from "@/lib/discriminatedUnion"
import {
  cardBlockRefsSchema,
  cardBlockSchema,
  transformCardBlock,
  transformCardBlockRefs,
} from "@/server/routers/contentstack/schemas/blocks/cardsGrid"
import * as pageLinks from "@/server/routers/contentstack/schemas/pageLinks"

import { removeMultipleSlashes } from "@/utils/url"

import { systemSchema } from "../schemas/system"

import { IconName } from "@/types/components/icon"
import { AlertTypeEnum } from "@/types/enums/alert"
import type { Image } from "@/types/image"

// Help me write this zod schema based on the type ContactConfig
export const validateContactConfigSchema = z.object({
  all_contact_config: z.object({
    items: z.array(
      z.object({
        email: z.object({
          name: z.string().nullable(),
          address: z.string().nullable(),
        }),
        email_loyalty: z.object({
          name: z.string().nullable(),
          address: z.string().nullable(),
        }),
        mailing_address: z.object({
          zip: z.string().nullable(),
          street: z.string().nullable(),
          name: z.string().nullable(),
          city: z.string().nullable(),
          country: z.string().nullable(),
        }),
        phone: z.object({
          number: z.string().nullable(),
          name: z.string().nullable(),
          footnote: z.string().nullable(),
        }),
        phone_loyalty: z.object({
          number: z.string().nullable(),
          name: z.string().nullable(),
          footnote: z.string().nullable(),
        }),
        visiting_address: z.object({
          zip: z.string().nullable(),
          country: z.string().nullable(),
          city: z.string().nullable(),
          street: z.string().nullable(),
        }),
      })
    ),
  }),
})

export enum ContactFieldGroupsEnum {
  email = "email",
  email_loyalty = "email_loyalty",
  mailing_address = "mailing_address",
  phone = "phone",
  phone_loyalty = "phone_loyalty",
  visiting_address = "visiting_address",
}

export type ContactFieldGroups = keyof typeof ContactFieldGroupsEnum

export type ContactConfigData = z.infer<typeof validateContactConfigSchema>

export type ContactConfig = ContactConfigData["all_contact_config"]["items"][0]

export type ContactFields = {
  display_text: string | null
  contact_field: string
  footnote: string | null
}

export const validateCurrentHeaderConfigSchema = z
  .object({
    all_current_header: z.object({
      items: z.array(
        z.object({
          frontpage_link_text: z.string(),
          logoConnection: z.object({
            edges: z.array(
              z.object({
                node: z.object({
                  description: z.string().optional().nullable(),
                  dimension: z.object({
                    height: z.number(),
                    width: z.number(),
                  }),
                  metadata: z.any().nullable(),
                  system: z.object({
                    uid: z.string(),
                  }),
                  title: z.string().optional().default(""),
                  url: z.string().optional().default(""),
                }),
              })
            ),
          }),
          menu: z.object({
            links: z.array(
              z.object({
                href: z.string(),
                title: z.string(),
              })
            ),
          }),
          top_menu: z.object({
            links: z.array(
              z.object({
                link: z.object({
                  href: z.string(),
                  title: z.string(),
                }),
                show_on_mobile: z.boolean(),
                sort_order_mobile: z.number(),
              })
            ),
          }),
        })
      ),
    }),
  })
  .transform((data) => {
    if (!data.all_current_header.items.length) {
      return {
        header: null,
      }
    }
    const header = data.all_current_header.items[0]
    return {
      header: {
        frontpageLinkText: header.frontpage_link_text,
        logo: header.logoConnection.edges[0].node,
        menu: header.menu,
        topMenu: header.top_menu,
      },
    }
  })

export interface GetCurrentHeaderData
  extends z.input<typeof validateCurrentHeaderConfigSchema> {}
export type HeaderData = z.output<typeof validateCurrentHeaderConfigSchema>

const validateCurrentHeaderRefConfigSchema = z.object({
  all_current_header: z.object({
    items: z.array(
      z.object({
        system: systemSchema,
      })
    ),
  }),
})

export type CurrentHeaderRefDataRaw = z.infer<
  typeof validateCurrentHeaderRefConfigSchema
>

const validateAppDownload = z.object({
  href: z.string(),
  imageConnection: z.object({
    edges: z.array(
      z.object({
        node: z.object({
          description: z.string().optional().nullable(),
          dimension: z.object({
            height: z.number(),
            width: z.number(),
          }),
          metadata: z.any().nullable(),
          system: z.object({
            uid: z.string(),
          }),
          title: z.string(),
          url: z.string(),
        }),
      })
    ),
  }),
})

const validateNavigationItem = z.object({
  links: z.array(z.object({ href: z.string(), title: z.string() })),
  title: z.string(),
})

export type NavigationItem = z.infer<typeof validateNavigationItem>

export const validateCurrentFooterConfigSchema = z.object({
  all_current_footer: z.object({
    items: z.array(
      z.object({
        title: z.string(),
        about: z.object({
          title: z.string(),
          text: z.string(),
        }),
        app_downloads: z.object({
          title: z.string(),
          app_store: validateAppDownload,
          google_play: validateAppDownload,
        }),
        logoConnection: z.object({
          edges: z.array(
            z.object({
              node: z.object({
                description: z.string().optional().nullable(),
                dimension: z.object({
                  height: z.number(),
                  width: z.number(),
                }),
                metadata: z.any().nullable(),
                system: z.object({
                  uid: z.string(),
                }),
                title: z.string(),
                url: z.string(),
              }),
            })
          ),
        }),
        navigation: z.array(validateNavigationItem),
        social_media: z.object({
          title: z.string(),
          facebook: z.object({ href: z.string(), title: z.string() }),
          instagram: z.object({ href: z.string(), title: z.string() }),
          twitter: z.object({ href: z.string(), title: z.string() }),
        }),
        trip_advisor: z.object({
          title: z.string(),
          logoConnection: z.object({
            edges: z.array(
              z.object({
                node: z.object({
                  description: z.string().optional().nullable(),
                  dimension: z.object({
                    height: z.number(),
                    width: z.number(),
                  }),
                  metadata: z.any().nullable(),
                  system: z.object({
                    uid: z.string(),
                  }),
                  title: z.string(),
                  url: z.string(),
                }),
              })
            ),
          }),
        }),
      })
    ),
  }),
})

export type CurrentFooterDataRaw = z.infer<
  typeof validateCurrentFooterConfigSchema
>

export type CurrentFooterData = Omit<
  CurrentFooterDataRaw["all_current_footer"]["items"][0],
  "logoConnection"
> & {
  logo: Image
}

const validateCurrentFooterRefConfigSchema = z.object({
  all_current_footer: z.object({
    items: z.array(
      z.object({
        system: systemSchema,
      })
    ),
  }),
})

export type CurrentFooterRefDataRaw = z.infer<
  typeof validateCurrentFooterRefConfigSchema
>

const validateExternalLink = z
  .object({
    href: z.string(),
    title: z.string(),
  })
  .optional()

const validateInternalLink = z
  .object({
    edges: z
      .array(
        z.object({
          node: z.object({
            system: z.object({
              uid: z.string(),
              locale: z.nativeEnum(Lang),
            }),
            url: z.string(),
            title: z.string(),
            web: z
              .object({
                original_url: z.string(),
              })
              .optional(),
          }),
        })
      )
      .max(1),
  })
  .transform((data) => {
    const node = data.edges[0]?.node
    if (!node) {
      return null
    }
    const url = node.url
    const originalUrl = node.web?.original_url
    const lang = node.system.locale

    return {
      url: originalUrl || removeMultipleSlashes(`/${lang}/${url}`),
      title: node.title,
    }
  })
  .optional()

export const validateLinkItem = z
  .object({
    title: z.string(),
    open_in_new_tab: z.boolean(),
    link: validateExternalLink,
    pageConnection: validateInternalLink,
  })
  .transform((data) => {
    return {
      url: data.pageConnection?.url ?? data.link?.href ?? "",
      title: data?.title ?? data.link?.title,
      openInNewTab: data.open_in_new_tab,
      isExternal: !data.pageConnection?.url,
    }
  })

const validateLinks = z
  .array(validateLinkItem)
  .transform((data) => data.filter((item) => item.url))

export const validateSecondaryLinks = z.array(
  z.object({
    title: z.string(),
    links: validateLinks,
  })
)

export const validateLinksWithType = z.array(
  z.object({
    type: z.string(),
    href: validateExternalLink,
  })
)

export const validateFooterConfigSchema = z
  .object({
    all_footer: z.object({
      items: z.array(
        z.object({
          main_links: validateLinks.default([]),
          app_downloads: z.object({
            title: z.string(),
            links: validateLinksWithType.default([]),
          }),
          secondary_links: validateSecondaryLinks.default([]),
          social_media: z.object({
            links: validateLinksWithType.default([]),
          }),
          tertiary_links: validateLinks.default([]),
        })
      ),
    }),
  })
  .transform((data) => {
    const {
      main_links,
      app_downloads,
      secondary_links,
      social_media,
      tertiary_links,
    } = data.all_footer.items[0]

    return {
      mainLinks: main_links,
      appDownloads: app_downloads,
      secondaryLinks: secondary_links,
      socialMedia: social_media,
      tertiaryLinks: tertiary_links,
    }
  })

const pageConnectionRefs = z.object({
  edges: z
    .array(
      z.object({
        node: z.object({
          system: systemSchema,
        }),
      })
    )
    .max(1),
})

export const validateFooterRefConfigSchema = z.object({
  all_footer: z.object({
    items: z
      .array(
        z.object({
          main_links: z
            .array(
              z.object({
                pageConnection: pageConnectionRefs,
              })
            )
            .nullable(),
          secondary_links: z
            .array(
              z.object({
                links: z.array(
                  z.object({
                    pageConnection: pageConnectionRefs,
                  })
                ),
              })
            )
            .nullable(),
          tertiary_links: z
            .array(
              z.object({
                pageConnection: pageConnectionRefs,
              })
            )
            .nullable(),
          system: systemSchema,
        })
      )
      .length(1),
  }),
})

/**
 * New Header Validation
 */

const linkRefsUnionSchema = z.discriminatedUnion("__typename", [
  pageLinks.contentPageRefSchema,
  pageLinks.hotelPageRefSchema,
  pageLinks.loyaltyPageRefSchema,
])

const linkRefsSchema = z
  .object({
    linkConnection: z.object({
      edges: z.array(
        z.object({
          node: linkRefsUnionSchema,
        })
      ),
    }),
  })
  .transform((data) => {
    if (data.linkConnection.edges.length) {
      const link = pageLinks.transformRef(data.linkConnection.edges[0].node)
      if (link) {
        return {
          link,
        }
      }
    }
    return { link: null }
  })

const menuItemsRefsSchema = z.intersection(
  linkRefsSchema,
  z
    .object({
      cardConnection: z.object({
        edges: z.array(
          z.object({
            node: cardBlockRefsSchema,
          })
        ),
      }),
      see_all_link: linkRefsSchema,
      submenu: z.array(
        z.object({
          links: z.array(linkRefsSchema),
        })
      ),
    })
    .transform((data) => {
      let card = null
      if (data.cardConnection.edges.length) {
        card = transformCardBlockRefs(data.cardConnection.edges[0].node)
      }

      return {
        card,
        see_all_link: data.see_all_link,
        submenu: data.submenu,
      }
    })
)

const topLinkRefsSchema = z.object({
  logged_in: linkRefsSchema.nullable(),
  logged_out: linkRefsSchema.nullable(),
})

export const headerRefsSchema = z
  .object({
    all_header: z.object({
      items: z
        .array(
          z.object({
            menu_items: z.array(menuItemsRefsSchema),
            system: systemSchema,
            top_link: topLinkRefsSchema,
          })
        )
        .max(1),
    }),
  })
  .transform((data) => {
    if (!data.all_header.items.length) {
      console.info(`Zod Error - No header returned in refs request`)
      throw new ZodError([
        {
          code: ZodIssueCode.custom,
          fatal: true,
          message: "No header returned (Refs)",
          path: ["all_header.items"],
        },
      ])
    }

    return {
      header: data.all_header.items[0],
    }
  })

const linkUnionSchema = z.discriminatedUnion("__typename", [
  pageLinks.accountPageSchema,
  pageLinks.contentPageSchema,
  pageLinks.hotelPageSchema,
  pageLinks.loyaltyPageSchema,
])

const linkSchema = z
  .object({
    linkConnection: z.object({
      edges: z.array(
        z.object({
          node: discriminatedUnion(linkUnionSchema.options),
        })
      ),
    }),
  })
  .transform((data) => {
    if (data.linkConnection.edges.length) {
      const linkNode = data.linkConnection.edges[0].node
      if (linkNode) {
        const link = pageLinks.transform(linkNode)
        if (link) {
          return {
            link,
          }
        }
      }
    }

    return {
      link: null,
    }
  })

const titleSchema = z.object({
  title: z.string().optional().default(""),
})

/**
 * Intersection has to be used since you are not
 * allowed to merge two schemas where one uses
 * transform
 */
const linkAndTitleSchema = z.intersection(linkSchema, titleSchema)

/**
 * Same as above 👆
 */
export const menuItemSchema = z
  .intersection(
    linkAndTitleSchema,
    z
      .object({
        cardConnection: z.object({
          edges: z.array(
            z.object({
              node: cardBlockSchema,
            })
          ),
        }),
        see_all_link: linkAndTitleSchema,
        submenu: z.array(
          z.object({
            links: z.array(linkAndTitleSchema),
            title: z.string().optional().default(""),
          })
        ),
      })
      .transform((data) => {
        let card = null
        if (data.cardConnection.edges.length) {
          card = transformCardBlock(data.cardConnection.edges[0].node)
        }

        return {
          card,
          seeAllLink: data.see_all_link,
          submenu: data.submenu,
        }
      })
  )
  .transform((data) => {
    return {
      ...data,
      link: data.submenu.length ? null : data.link,
      seeAllLink: data.submenu.length ? data.seeAllLink : null,
    }
  })

const topLinkItemSchema = z.intersection(
  linkAndTitleSchema,
  z.object({
    icon: z
      .enum(["loyalty", "info", "offer"])
      .nullable()
      .transform((icon) => {
        switch (icon) {
          case "loyalty":
            return IconName.Gift
          case "info":
            return IconName.InfoCircle
          case "offer":
            return IconName.PriceTag
          default:
            return null
        }
      }),
  })
)

export const topLinkSchema = z.object({
  logged_in: topLinkItemSchema.nullable(),
  logged_out: topLinkItemSchema.nullable(),
})

export const headerSchema = z
  .object({
    all_header: z.object({
      items: z
        .array(
          z.object({
            menu_items: z.array(menuItemSchema),
            top_link: topLinkSchema,
          })
        )
        .max(1),
    }),
  })
  .transform((data) => {
    if (!data.all_header.items.length) {
      console.info(`Zod Error - No header returned in request`)
      throw new ZodError([
        {
          code: ZodIssueCode.custom,
          fatal: true,
          message: "No header returned",
          path: ["all_header.items"],
        },
      ])
    }

    const header = data.all_header.items[0]
    return {
      header: {
        menuItems: header.menu_items,
        topLink: header.top_link,
      },
    }
  })

export const alertSchema = z
  .object({
    type: z.nativeEnum(AlertTypeEnum),
    text: z.string(),
    heading: z.string(),
    phone_contact: z.object({
      display_text: z.string(),
      phone_number: z.string().nullable(),
      footnote: z.string().nullable(),
    }),
    has_link: z.boolean(),
    link: linkAndTitleSchema,
    has_sidepeek_button: z.boolean(),
    sidepeek_button: z.object({
      cta_text: z.string(),
    }),
    sidepeek_content: z.object({
      heading: z.string(),
      content: z.object({
        json: z.any(),
        embedded_itemsConnection: z.object({
          edges: z.array(
            z.object({
              node: z
                .discriminatedUnion("__typename", [
                  pageLinks.accountPageSchema,
                  pageLinks.contentPageSchema,
                  pageLinks.hotelPageSchema,
                  pageLinks.loyaltyPageSchema,
                ])
                .transform((data) => {
                  const link = pageLinks.transform(data)
                  if (link) {
                    return link
                  }
                  return data
                }),
            })
          ),
        }),
      }),
    }),
  })
  .transform(
    ({
      type,
      heading,
      text,
      phone_contact,
      has_link,
      link,
      has_sidepeek_button,
      sidepeek_button,
      sidepeek_content,
    }) => {
      const hasLink = has_link && link.link
      return {
        type,
        text,
        heading,
        phoneContact:
          phone_contact.display_text && phone_contact.phone_number
            ? {
                displayText: phone_contact.display_text,
                phoneNumber: phone_contact.phone_number,
                footnote: phone_contact.footnote,
              }
            : null,
        hasSidepeekButton: !!has_sidepeek_button,
        link: hasLink
          ? {
              url: link.link.url,
              title: link.title,
            }
          : null,
        sidepeekButton:
          !hasLink && has_sidepeek_button ? sidepeek_button : null,
        sidepeekContent:
          !hasLink && has_sidepeek_button ? sidepeek_content : null,
      }
    }
  )

export const siteConfigSchema = z
  .object({
    all_site_config: z.object({
      items: z
        .array(
          z.object({
            sitewide_alert: z.object({
              booking_widget_disabled: z.boolean(),
              alertConnection: z.object({
                edges: z
                  .array(
                    z.object({
                      node: alertSchema,
                    })
                  )
                  .max(1),
              }),
            }),
          })
        )
        .max(1),
    }),
  })
  .transform((data) => {
    if (!data.all_site_config.items.length) {
      return {
        sitewideAlert: null,
        bookingWidgetDisabled: false,
      }
    }

    const { sitewide_alert } = data.all_site_config.items[0]

    return {
      sitewideAlert: sitewide_alert.alertConnection.edges[0]?.node || null,
      bookingWidgetDisabled: sitewide_alert.booking_widget_disabled,
    }
  })

const sidepeekContentRefSchema = z.object({
  content: z.object({
    embedded_itemsConnection: z.object({
      edges: z.array(
        z.object({
          node: z.discriminatedUnion("__typename", [
            pageLinks.accountPageRefSchema,
            pageLinks.contentPageRefSchema,
            pageLinks.hotelPageRefSchema,
            pageLinks.loyaltyPageRefSchema,
          ]),
        })
      ),
    }),
  }),
})

const alertConnectionRefSchema = z.object({
  edges: z.array(
    z.object({
      node: z.object({
        link: linkRefsSchema,
        sidepeek_content: sidepeekContentRefSchema,
        system: systemSchema,
      }),
    })
  ),
})

export const siteConfigRefSchema = z.object({
  all_site_config: z.object({
    items: z.array(
      z.object({
        sitewide_alert: z.object({
          alertConnection: alertConnectionRefSchema,
        }),
        system: systemSchema,
      })
    ),
  }),
})
