<script lang="ts">
	import { onDestroy } from 'svelte';
	import Composite from './Composite.svelte';
	import components from '$lib/core/componentLoader';
	import services from '$lib/core/servicesLoader';
	import { dataStore } from '$lib/core/dataLoader';
	import { createCompositeStore, getCompositeStore } from '$lib/core/compositeStores';

	interface IComposite {
		layout?: ICompositeLayout;
		id: string;
		slot?: string;
		component: string;
		services?: string[];
		map?: Record<string, string>;
		store?: Record<string, any>;
		children?: IComposite[];
	}

	interface ICompositeLayout {
		areas: string;
		columns?: string;
		rows?: string;
		gap?: string;
		tailwindClasses?: string;
	}

	export let composite: IComposite;

	let loadedServices: Record<string, any> = {};

	// Reactive loading mechanism for services based on composite changes
	$: if (composite?.services) {
		composite.services.forEach(async (serviceName) => {
			if (!loadedServices[serviceName]) {
				loadedServices[serviceName] = await loadService(serviceName);
			}
		});
	}

	$: layoutStyle = composite?.layout
		? `
		grid-template-areas: ${composite.layout.areas};
		${composite.layout.gap ? `gap: ${composite.layout.gap};` : ''}
		${composite.layout.columns ? `grid-template-columns: ${composite.layout.columns};` : ''}
		${composite.layout.rows ? `grid-template-rows: ${composite.layout.rows};` : ''}
		`
		: '';

	// Reactive statement to watch changes in the composite prop
	$: if (composite?.children) {
		composite.children.forEach((child) => {
			initializeCompositeState(child);
			mapAndSubscribe(child);
		});
	}

	function initializeCompositeState(child: IComposite) {
		if (child.id) {
			child.store = createCompositeStore(child.id, child.store || {});
		}

		if (child.children) {
			child.children.forEach(initializeCompositeState);
		}
	}

	function mapAndSubscribe(component: IComposite) {
		console.log('Mapping and subscribing for:', component.id);

		if (component.map) {
			const localStore = getCompositeStore(component.id);

			for (const [localKey, external] of Object.entries(component.map)) {
				const [externalID, externalKey] = external.split('.').map((item) => item.trim());

				if (externalID === 'data') {
					const unsubscribe = dataStore.subscribe((store) => {
						if (externalKey in store) {
							// Check if the data item is a Svelte store
							if (store[externalKey] && typeof store[externalKey].subscribe === 'function') {
								let innerUnsub = store[externalKey].subscribe((value) => {
									localStore.update((storeValue) => {
										return { ...storeValue, [localKey]: value };
									});
								});
								onDestroy(innerUnsub);
							} else {
								localStore.update((storeValue) => {
									return { ...storeValue, [localKey]: store[externalKey] };
								});
							}
						}
					});

					onDestroy(unsubscribe);
				} else {
					const externalStore = getCompositeStore(externalID);
					if (externalStore) {
						const unsubscribe = externalStore.subscribe((externalState) => {
							if (externalState && externalKey in externalState) {
								localStore.update((storeValue) => {
									return { ...storeValue, [localKey]: externalState[externalKey] };
								});
							}
						});

						onDestroy(unsubscribe);
					}
				}
			}
		}

		if (component.children) {
			component.children.forEach(mapAndSubscribe);
		}
	}

	async function getComponent(componentName: string) {
		if (components[componentName]) {
			const module = await components[componentName]();
			return module.default;
		}
		return null;
	}

	async function loadService(serviceName: string) {
		if (services[serviceName]) {
			const module = await services[serviceName]();
			return module.default || module;
		}
		return null;
	}

	async function getServiceProps(component: IComposite) {
		const loadedServices = {};
		if (component.services) {
			for (const serviceName of component.services) {
				const loadedService = await loadService(serviceName);
				if (loadedService) {
					loadedServices[serviceName] = loadedService;
				}
			}
		}
		return loadedServices;
	}

	async function loadComponentAndService(component: IComposite) {
		return await Promise.all([getComponent(component.component), getServiceProps(component)]);
	}
</script>

<div class={`grid w-full h-full ${composite?.layout?.tailwindClasses || ''}`} style={layoutStyle}>
	{#if composite && 'component' in composite}
		{#await loadComponentAndService(composite) then [Component]}
			{#if Component}
				<svelte:component
					this={Component}
					id={composite.id}
					store={getCompositeStore(composite.id)}
					services={loadedServices}
				/>
			{:else if composite.component}
				<p>Component {composite.component} not found.</p>
			{/if}
		{/await}
	{/if}

	{#if composite?.children}
		{#each composite.children as child (child.id)}
			<div class="w-full h-full overflow-hidden" style={`grid-area: ${child.slot}`}>
				{#await loadComponentAndService(child) then [ChildComponent]}
					{#if ChildComponent}
						<svelte:component
							this={ChildComponent}
							id={child.id}
							store={getCompositeStore(child.id)}
							services={loadedServices}
						/>
						{#if child.children && child.children.length}
							<Composite composite={child} />
						{/if}
					{:else if child.component}
						<p>Component {child.component} not found.</p>
					{/if}
				{/await}
			</div>
		{/each}
	{/if}
</div>
