import type { ArrayFieldVal, Attributes, BaseBagelField, BglFormSchemaT, FieldByP, IconType, InputBagelField, Option, Path, SchemaChild, SelectBagelField, ShallowBglFormSchemaT, UploadInputProps } from '@bagelink/vue'
import type { DefaultPathsOptions, PathsOptions } from 'type-fest/source/paths'

interface InputOptions<
	T,
	P extends Path<T>
> extends Partial<BaseBagelField<T, P>> {
	defaultValue?: string | number
	autocomplete?: AutoFillField
}

interface DateOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	enableTime?: boolean
	mode?: 'day' | 'month' | 'year'
	locale?: string
	timezone?: string
}

interface TextInputOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	type?: 'text' | 'tel' | 'email' | 'time'
	pattern?: string
	multiline?: boolean
	autoheight?: boolean
	code?: boolean
	rows?: number | string
	icon?: IconType
	iconStart?: IconType
}

interface SlctInputOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	searchable?: boolean
	multiselect?: boolean
	clearable?: boolean
	onSearch?: (search: string) => any
}

interface NumFieldOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	max?: number
	min?: number
	step?: number
	layout?: 'default' | 'vertical' | 'horizontal'
	padZero?: number
	center?: boolean
	useGrouping?: boolean
}

type RichTextOptions<
	T,
	P extends Path<T>
> = InputOptions<T, P>

export function getBaseField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	labelOrRest: string | Partial<BaseBagelField<T, P>> = {},
	rest: Partial<BaseBagelField<T, P>> = {}
): BaseBagelField<T, P, PO> {
	if (typeof labelOrRest === 'object') return { id, ...labelOrRest }
	return { id, label: labelOrRest, ...rest }
}

export function richText<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: RichTextOptions<T, P>,
): BaseBagelField<T, P, PO> {
	return {
		$el: 'richtext',
		class: options?.class,
		required: options?.required,
		id,
		label,
		vIf: options?.vIf,
		placeholder: options?.placeholder,
		attrs: {
			autocomplete: options?.autocomplete,
		},
	}
}

export function txtField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: TextInputOptions<T, P>,
): InputBagelField<T, P, PO> {
	return {
		$el: 'text',
		id,
		label,
		class: options?.class,
		required: options?.required,
		vIf: options?.vIf,
		disabled: options?.disabled,
		placeholder: options?.placeholder,
		defaultValue: options?.defaultValue,
		validate: options?.validate,
		onUpdate: options?.onUpdate,
		attrs: {
			type: options?.type,
			pattern: options?.pattern,
			multiline: options?.multiline,
			autocomplete: options?.autocomplete,
			rows: options?.rows,
			code: options?.code,
			autoheight: options?.autoheight,
			icon: options?.icon,
			iconStart: options?.iconStart,
		},
	}
}

export function selectField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: Option[] | (() => Option[]),
	config?: SlctInputOptions<T, P>,
): SelectBagelField<T, P, PO> {
	return {
		$el: 'select',
		id,
		options,
		class: config?.class,
		placeholder: config?.placeholder,
		required: config?.required,
		label,
		defaultValue: config?.defaultValue,
		vIf: config?.vIf,
		attrs: {
			disabled: config?.disabled,
			searchable: config?.searchable,
			multiselect: config?.multiselect,
			onSearch: config?.onSearch,
			clearable: config?.clearable,
			autocomplete: config?.autocomplete,
		},
	}
}

export const slctField = selectField

interface CheckInputOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	value?: string
}

export function checkField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: CheckInputOptions<T, P>,
): BaseBagelField<T, P, PO> {
	return {
		$el: 'check',
		class: options?.class,
		required: options?.required,
		id,
		label,
		attrs: {
			value: options?.value,
		},
	}
}

interface EmailInputOptions<
	T,
	P extends Path<T>
> extends InputOptions<T, P> {
	autocorrect?: boolean
	serverValidate?: boolean
	preventFakeEmails?: boolean
	pattern?: string
}

export function emailField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(id?: P, label?: string, options?: EmailInputOptions<T, P>): BaseBagelField<T, P, PO> {
	return {
		$el: 'email',
		id,
		label,
		class: options?.class,
		required: options?.required,
		vIf: options?.vIf,
		attrs: {
			autocorrect: options?.autocorrect,
			serverValidate: options?.serverValidate,
			preventFakeEmails: options?.preventFakeEmails,
			pattern: options?.pattern,
		},
	}
}

export function dateField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: DateOptions<T, P>,
): BaseBagelField<T, P, PO> {
	return {
		$el: 'date',
		class: options?.class,
		required: options?.required,
		id,
		disabled: options?.disabled,
		label,
		defaultValue: options?.defaultValue,
		vIf: options?.vIf,
		attrs: {
			disabled: options?.disabled,
			autocomplete: options?.autocomplete,
			enableTime: options?.enableTime,
			mode: options?.mode,
			locale: options?.locale,
			timezone: options?.timezone,
		},
	}
}

export function numField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: NumFieldOptions<T, P>,
): BaseBagelField<T, P, PO> {
	return {
		$el: 'number',
		class: options?.class,
		required: options?.required,
		defaultValue: options?.defaultValue,
		id,
		label,
		disabled: options?.disabled,
		placeholder: options?.placeholder,
		helptext: options?.helptext,
		vIf: options?.vIf,
		attrs: {
			autocomplete: options?.autocomplete,
			step: options?.step,
			min: options?.min,
			max: options?.max,
			layout: options?.layout,
			padZero: options?.padZero,
			center: options?.center,
			useGrouping: options?.useGrouping,
		},
	}
}

export function frmRow<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	...children: SchemaChild<T, P, PO>[]
): FieldByP<T, P, PO> {
	return {
		$el: 'div',
		class: 'flex gap-1 m_block align-items-end',
		children,
	}
}

export interface UploadOptions<T, K extends Path<T>> extends Omit<UploadInputProps, 'id'>, InputOptions<T, K> {}

export function uploadField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: UploadOptions<T, P>
): BaseBagelField<T, P, PO> {
	return {
		$el: 'upload',
		id,
		label,
		vIf: options?.vIf,
		attrs: {
			...options,
		},
	}
}

interface RangeOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	min?: number
	max?: number
	step?: number
	rtl?: boolean
	multiRange?: boolean
	formatValue?: (value: number) => string
}

export function rangeField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: RangeOptions <T, P>
): BaseBagelField<T, P, PO> {
	return {
		$el: 'range',
		id,
		label,
		vIf: options?.vIf,
		attrs: {
			autocomplete: options?.autocomplete,
			min: options?.min,
			max: options?.max,
			step: options?.step,
			rtl: options?.rtl,
			multiRange: options?.multiRange,
			formatValue: options?.formatValue,
		},
	}
}

export function bglForm<T>(idOrField?: string | FieldByP<T, Path<T>>, ...schema: FieldByP<T, Path<T>>[]) {
	if (typeof idOrField === 'string') {
		return {
			$el: 'bagelform',
			id: idOrField,
			attrs: {
				schema: [idOrField, ...schema],
			},
		}
	}
	return {
		$el: 'bagelform',
		attrs: {
			schema: [idOrField, ...schema]
		},
	}
}

export function telField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: { [key: string]: any }
): BaseBagelField<T, P, PO> {
	return {
		$el: 'tel',
		id,
		label,
		vIf: options?.vIf,
		attrs: options,
		class: options?.class,
	}
}

export function colorField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id?: P,
	label?: string,
	options?: { [key: string]: any }
): BaseBagelField<T, P, PO> {
	return {
		$el: 'color',
		id,
		label,
		vIf: options?.vIf,
		attrs: options,
		class: options?.class,
	}
}

export function findBglFieldById<T>(id: string, _schema: ShallowBglFormSchemaT<T>): FieldByP<T, Path<T>> | undefined {
	for (const field of _schema) {
		if (field.id === id) return field as FieldByP<T, Path<T>> | undefined
		if (field.children && Number(field.children.length) > 0) {
			const fieldChildren = field.children.filter(
				c => typeof c === 'object' && '$el' in c
			)
			const child = findBglFieldById(id, fieldChildren as ShallowBglFormSchemaT<T>)
			if (child) return child as FieldByP<T, Path<T>> | undefined
		}
	}
	return undefined
}

export interface ArrayFieldOptions<T, K extends Path<T>> extends InputOptions<T, K> {
	delete?: boolean
	add?: boolean
}

export type ArrayType = 'number' | 'text'

export function arrField<
	T,
	P extends Path<T, PO>,
	PO extends PathsOptions = DefaultPathsOptions,
>(
	id: P,
	label: string,
	schemaOrType: BglFormSchemaT<ArrayFieldVal<T, P>> | ArrayType,
	options?: ArrayFieldOptions<T, P>
): BaseBagelField<T, P, PO> {
	const attrs: Attributes<T, P> = { delete: true, add: true, ...options }

	if (typeof schemaOrType === 'string') {
		attrs.type = schemaOrType
	} else {
		attrs.schema = schemaOrType
	}

	return {
		label,
		id,
		class: options?.class,
		$el: 'array',
		vIf: options?.vIf,
		attrs,
	}
}
// export function updateBagelSchemaWithErrorResponse<T>(
// 	schema: BglFormSchemaT<T>,
// 	invalidFieldId: Record<string, string[]>,
// ) {
// 	return schema.map((f) => {
// 				if (f.id === invalidFieldId) {
// 					f.class = 'bagel-input-error'
// 				}
// 				if (f.id === undefined && f.children) {
// 					f.children = f.children.map((c) => {
// 						if (c.id === invalidFieldId) {
// 							c.class = 'bagel-input-error'
// 						}
// 						return c
// 					})
// 				}
// 				return f
// 			})
// }

export function useForm() {
	return {
		txtField,
		selectField,
		checkField,
		dateField,
		numField,
		frmRow,
		uploadField,
		rangeField,
		bglForm,
		telField,
		colorField,
		arrField,
		richText,
		findBglFieldById,
		getBaseField
	}
}
