import type { SessionPayload } from './lib/types';

export type PreviewDraft = {
	form_id: number;
	steps?: readonly string[];
	service_ids?: readonly number[] | null;
	agent_ids?: readonly number[] | null;
	location_ids?: readonly number[] | null;
	category_ids?: readonly number[] | null;
	settings?: Record< string, unknown >;
	theme?: Record< string, string >;
	timezone?: string;
	locale?: string;
};

export type WidgetConfig = {
	restUrl: string;
	nonce: string;
	formId: number;
	timezone: string;
	locale?: string;
	theme?: Record< string, string >;
	previewDraft?: PreviewDraft;
	entitlements?: readonly string[];
	paypalClientId?: string;
	paypalSandbox?: boolean;
};

export async function api< T >(
	config: WidgetConfig,
	path: string,
	init: RequestInit = {}
): Promise< T > {
	const headers: Record< string, string > = {
		'Content-Type': 'application/json',
		'X-WP-Nonce': config.nonce,
		...( init.headers as Record< string, string > | undefined ),
	};

	const response = await fetch( `${ config.restUrl }${ path }`, {
		...init,
		headers,
	} );

	const body = ( await response.json() ) as T & {
		code?: string;
		message?: string;
	};

	if ( ! response.ok ) {
		throw { status: response.status, body };
	}

	return body;
}

export function createSession(
	config: WidgetConfig
): Promise< SessionPayload > {
	return api< SessionPayload >( config, '/session', {
		method: 'POST',
		body: JSON.stringify( {
			form_id: config.formId,
			timezone: config.timezone,
		} ),
	} );
}

export function createPreviewSession(
	config: WidgetConfig
): Promise< SessionPayload > {
	const draft = config.previewDraft ?? { form_id: config.formId };
	return api< SessionPayload >(
		config,
		'/admin/booking-forms/preview-session',
		{
			method: 'POST',
			body: JSON.stringify( {
				...draft,
				form_id: draft.form_id ?? config.formId,
				timezone: draft.timezone ?? config.timezone,
			} ),
		}
	);
}

export function getSession(
	config: WidgetConfig,
	token: string
): Promise< SessionPayload > {
	return api< SessionPayload >(
		config,
		`/session/${ encodeURIComponent( token ) }`
	);
}

export function submitStep(
	config: WidgetConfig,
	token: string,
	step: string,
	payload: Record< string, unknown >
): Promise< SessionPayload > {
	// The BE registers `/session/{token}/step/{name}` per docs/07 §5a.
	// The step identifier is in the URL; the request body carries the step's
	// fields directly (no extra wrapping object).
	return api< SessionPayload >(
		config,
		`/session/${ encodeURIComponent( token ) }/step/${ encodeURIComponent(
			step
		) }`,
		{
			method: 'POST',
			body: JSON.stringify( payload ),
		}
	);
}

export function goBack(
	config: WidgetConfig,
	token: string
): Promise< SessionPayload > {
	return api< SessionPayload >(
		config,
		`/session/${ encodeURIComponent( token ) }/back`,
		{
			method: 'POST',
			body: '{}',
		}
	);
}

export function submitBooking(
	config: WidgetConfig,
	token: string
): Promise< SessionPayload > {
	return api< SessionPayload >(
		config,
		`/session/${ encodeURIComponent( token ) }/submit`,
		{
			method: 'POST',
			body: '{}',
		}
	);
}

export type PaymentIntentPayload = {
	gateway_intent_id: string;
	client_secret: string;
	publishable_key: string;
	transaction_uuid: string;
};

export function createPaymentIntent(
	config: WidgetConfig,
	token: string,
	gateway = 'stripe',
	paymentType = 'full'
): Promise< PaymentIntentPayload > {
	return api< PaymentIntentPayload >( config, '/payment/intent', {
		method: 'POST',
		body: JSON.stringify( {
			token,
			gateway,
			payment_type: paymentType,
		} ),
	} );
}

export function validateCoupon(
	config: WidgetConfig,
	token: string,
	code: string
): Promise< { totals?: Record< string, unknown >; code?: string } > {
	return api( config, '/coupon/validate', {
		method: 'POST',
		body: JSON.stringify( { token, code } ),
	} );
}

export type ServiceItem = {
	id: number;
	name: string;
	description?: string;
	default_duration_id?: number;
	durations?: Array< {
		id: number;
		minutes: number;
		price?: { amount: number; currency: string };
	} >;
};

export function listServices(
	config: WidgetConfig
): Promise< { items?: ServiceItem[]; services?: ServiceItem[] } > {
	return api( config, '/services' );
}

export type AvailabilitySlot = {
	start?: string | { utc?: string; local?: string };
	start_utc?: string;
	start_local?: string;
	is_available?: boolean;
	available?: boolean;
};

export function fetchAvailability(
	config: WidgetConfig,
	params: {
		serviceId: number;
		durationId: number;
		dateFrom: string;
		dateTo: string;
		timezone: string;
		attendees?: number;
	}
): Promise< {
	slots?: AvailabilitySlot[];
	days?: Array< { date: string; slots: AvailabilitySlot[] } >;
} > {
	const q = new URLSearchParams( {
		service_id: String( params.serviceId ),
		duration_id: String( params.durationId ),
		date_from: params.dateFrom,
		date_to: params.dateTo,
		timezone: params.timezone,
	} );
	if ( params.attendees !== undefined ) {
		q.set( 'attendees', String( params.attendees ) );
	}
	return api( config, `/availability?${ q }` );
}

export type WidgetApiErrorBody = {
	code?: string;
	message?: string;
	data?: {
		status?: number;
		fields?: Record< string, string >;
		notices?: string[];
	};
};

/**
 * Per-field errors from a 422 step submission.
 *
 * @param err Thrown API error.
 */
export function stepFieldErrors(
	err: unknown
): Record< string, string > | undefined {
	const body = ( err as { body?: WidgetApiErrorBody } ).body;
	const fields = body?.data?.fields;
	if ( ! fields || 'object' !== typeof fields ) {
		return undefined;
	}
	const out: Record< string, string > = {};
	for ( const [ key, value ] of Object.entries( fields ) ) {
		if ( 'string' === typeof value && '' !== value ) {
			out[ key ] = value;
		}
	}
	return Object.keys( out ).length > 0 ? out : undefined;
}

/**
 * Human-readable step error. Field-level 422s are summarised so the banner
 * does not repeat "Step validation failed."
 *
 * @param err Thrown API error.
 */
export function stepErrorMessage( err: unknown ): string {
	const fields = stepFieldErrors( err );
	if ( fields ) {
		return 'Please correct the highlighted fields.';
	}
	const body = ( err as { body?: WidgetApiErrorBody } ).body;
	const notices = body?.data?.notices?.filter(
		( n ) => 'string' === typeof n && '' !== n
	);
	if ( notices && notices.length > 0 ) {
		return notices.join( ' ' );
	}
	return body?.message ?? 'That step could not be saved. Please try again.';
}
