import * as prismic from "@prismicio/client"

import { sentenceCase } from "../lib/changeCase"
import { createFaker } from "../lib/createFaker"
import * as modelGen from "../model"
import type { MockValueStateConfig, MockValueConfig } from "../types"
import { contentRelationship } from "./contentRelationship"
import { linkToMedia } from "./linkToMedia"

export type MockLinkValueConfig<
	LinkType extends (typeof prismic.LinkType)[keyof typeof prismic.LinkType] =
		(typeof prismic.LinkType)[keyof typeof prismic.LinkType],
	Model extends prismic.CustomTypeModelLinkField = prismic.CustomTypeModelLinkField,
	State extends prismic.FieldState = prismic.FieldState,
> = {
	type?: LinkType
	withTargetBlank?: NonNullable<Model["config"]>["allowTargetBlank"] extends undefined
		? false
		: boolean
	withText?: NonNullable<Model["config"]>["allowText"] extends undefined ? false : boolean
	/** A list of potential documents to which the field can be linked. */
	linkableDocuments?: LinkType extends typeof prismic.LinkType.Document
		? prismic.PrismicDocument[]
		: never
} & MockValueConfig<Model> &
	MockValueStateConfig<State>

export type MockLinkValue<
	LinkType extends (typeof prismic.LinkType)[keyof typeof prismic.LinkType] =
		(typeof prismic.LinkType)[keyof typeof prismic.LinkType],
	State extends prismic.FieldState = "filled",
> = State extends "empty"
	? prismic.EmptyLinkField<LinkType>
	: LinkType extends typeof prismic.LinkType.Web
		? prismic.FilledLinkToWebField
		: LinkType extends typeof prismic.LinkType.Media
			? prismic.FilledLinkToMediaField
			: LinkType extends typeof prismic.LinkType.Document
				? prismic.FilledContentRelationshipField
				: never

export const link = <
	LinkType extends (typeof prismic.LinkType)[keyof typeof prismic.LinkType] =
		(typeof prismic.LinkType)[keyof typeof prismic.LinkType],
	Model extends prismic.CustomTypeModelLinkField = prismic.CustomTypeModelLinkField,
	State extends prismic.FieldState = "filled",
>(
	config: MockLinkValueConfig<LinkType, Model, State>,
): MockLinkValue<LinkType, State> => {
	const faker = config.faker || createFaker(config.seed)

	const type =
		config.type ||
		faker.randomElement([prismic.LinkType.Web, prismic.LinkType.Document, prismic.LinkType.Media])

	if (config.state === "empty") {
		return {
			link_type: type,
		} as MockLinkValue<LinkType, State>
	} else {
		switch (type) {
			case prismic.LinkType.Document: {
				return contentRelationship({
					faker,
					state: config.state,
					linkableDocuments: config.linkableDocuments,
				}) as unknown as MockLinkValue<LinkType, State>
			}

			case prismic.LinkType.Media: {
				return linkToMedia({
					faker,
					state: config.state,
					withText: config.withText,
				}) as MockLinkValue<LinkType, State>
			}

			case prismic.LinkType.Web:
			default: {
				const model = config.model || modelGen.link({ faker })

				return {
					link_type: prismic.LinkType.Web,
					url: faker.url(),
					target:
						(config.withTargetBlank ?? (model.config?.allowTargetBlank && faker.boolean()))
							? "_blank"
							: undefined,
					...((config.withText ?? (model.config?.allowText && faker.boolean()))
						? { text: sentenceCase(faker.words(2)) }
						: {}),
				} as MockLinkValue<LinkType, State>
			}
		}
	}
}
