<script lang="ts">
	import Composite from './Composite.svelte';
	import components from '$lib/core/componentLoader';
	import services from '$lib/core/servicesLoader';
	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;

	$: 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};` : ''}
`
		: '';

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

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

	$: if (composite && composite.children) {
		composite.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());
				const externalStore = getCompositeStore(externalID);
				if (externalStore) {
					externalStore.subscribe((externalState) => {
						console.log('External state:', externalState);
						if (externalState && externalKey in externalState) {
							localStore.update((storeValue) => {
								storeValue = storeValue || {};
								if (storeValue[localKey] !== externalState[externalKey]) {
									storeValue[localKey] = externalState[externalKey];
									return storeValue;
								}
								return storeValue;
							});
						}
					});
				}
			}
		}

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

	$: if (composite && composite.children) {
		composite.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;
	}
	// Additional utility function to get component and service props
	async function loadComponentAndService(component: IComposite) {
		return await Promise.all([getComponent(component.component), getServiceProps(component)]);
	}
</script>

<div
	class={composite?.layout
		? `grid w-full h-full ${composite.layout.tailwindClasses}`
		: 'grid w-full h-full'}
	style={layoutStyle}
>
	{#if composite && 'component' in composite}
		{#await loadComponentAndService(composite) then [Component, serviceProps]}
			{#if Component}
				<svelte:component
					this={Component}
					id={composite.id}
					store={getCompositeStore(composite.id)}
					services={serviceProps}
				/>
			{: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, childServiceProps]}
					{#if ChildComponent}
						<svelte:component
							this={ChildComponent}
							id={child.id}
							store={getCompositeStore(child.id)}
							services={childServiceProps}
						/>
						{#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>
