import { useState, useEffect } from '@wordpress/element';
import {
	Button,
	SelectControl,
	TextControl,
	CheckboxControl,
	Spinner,
} from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';

const JOIN_TYPES = [
	{ label: 'Inner (only matching rows)', value: 'inner' },
	{ label: 'Left (all primary, matched target)', value: 'left' },
	{ label: 'Right (all target, matched primary)', value: 'right' },
];

const JOINABLE_BLOCK_TYPES = [
	{ label: 'Posts', value: 'posts' },
	{ label: 'Users', value: 'users' },
	{ label: 'Comments', value: 'comments' },
];

export default function JoinSelector( { blockConfig, blockType, onChange } ) {
	const joins = blockConfig.joins || [];
	const postTypes = window.wlconduitData?.postTypes || [];

	function updateJoin( index, partial ) {
		const updated = [ ...joins ];
		updated[ index ] = { ...updated[ index ], ...partial };
		onChange( updated );
	}

	function addJoin() {
		onChange( [
			...joins,
			{
				label: '',
				target_type: 'posts',
				target_post_type: 'post',
				local_field: '',
				local_field_source: 'native',
				local_field_from: 'self',
				target_field: '',
				target_field_source: 'native',
				join_type: 'inner',
				target_fields: [],
				target_meta_keys: {},
			},
		] );
	}

	function removeJoin( index ) {
		const updated = joins.filter( ( _, i ) => i !== index );
		onChange( updated );
	}

	return (
		<div className="aic-join-selector">
			<p className="aic-selector-hint">
				Join data from other tables into each result row. Chain multiple joins for complex relationships.
			</p>

			{ joins.map( ( join, index ) => (
				<JoinEntry
					key={ index }
					index={ index }
					join={ join }
					joins={ joins }
					blockType={ blockType }
					blockConfig={ blockConfig }
					postTypes={ postTypes }
					onChange={ ( partial ) => updateJoin( index, partial ) }
					onRemove={ () => removeJoin( index ) }
				/>
			) ) }

			<Button variant="secondary" onClick={ addJoin }>
				+ Add Join
			</Button>
		</div>
	);
}

function JoinEntry( { index, join, joins, blockType, blockConfig, postTypes, onChange, onRemove } ) {
	const [ myFieldOptions, setMyFieldOptions ] = useState( [] );
	const [ targetColumns, setTargetColumns ] = useState( {} );
	const [ targetMeta, setTargetMeta ] = useState( [] );
	const [ targetFields, setTargetFields ] = useState( {} );
	const [ loadingTarget, setLoadingTarget ] = useState( false );

	// Build "My field" options from primary block + previous joins.
	useEffect( () => {
		const options = [];

		apiFetch( { path: `wlconduit/v1/join-columns?block_type=${ blockType }` } )
			.then( ( columns ) => {
				const group = { label: 'Primary', options: [] };
				Object.entries( columns ).forEach( ( [ col, label ] ) => {
					group.options.push( { label: `${ label } (${ col })`, value: `self:${ col }` } );
				} );

				options.push( group );

				// Previous joins' columns.
				const promises = [];
				for ( let i = 0; i < index; i++ ) {
					const prevJoin = joins[ i ];
					if ( prevJoin && prevJoin.target_type ) {
						promises.push(
							apiFetch( { path: `wlconduit/v1/join-columns?block_type=${ prevJoin.target_type }` } )
								.then( ( cols ) => ( { index: i, join: prevJoin, columns: cols } ) )
						);
					}
				}

				return Promise.all( promises );
			} )
			.then( ( prevResults ) => {
				if ( prevResults ) {
					prevResults.forEach( ( prev ) => {
						const groupLabel = prev.join.label || `Join ${ prev.index + 1 }`;
						const group = { label: groupLabel, options: [] };

						Object.entries( prev.columns ).forEach( ( [ col, label ] ) => {
							group.options.push( {
								label: `${ label } (${ col })`,
								value: `${ prev.index }:${ col }`,
							} );
						} );

						options.push( group );
					} );
				}

				setMyFieldOptions( options );
			} )
			.catch( () => {} );
	}, [ blockType, blockConfig.post_type, blockConfig.meta_keys, index, joins.length ] );

	// Load target data when target type or post type changes.
	useEffect( () => {
		if ( ! join.target_type ) {
			return;
		}

		setLoadingTarget( true );

		// Load native columns.
		const columnsPromise = apiFetch( { path: `wlconduit/v1/join-columns?block_type=${ join.target_type }` } )
			.then( ( data ) => setTargetColumns( data || {} ) )
			.catch( () => setTargetColumns( {} ) );

		// Load available fields from block definition.
		const blockDef = window.wlconduitData?.blocks?.[ join.target_type ];
		setTargetFields( blockDef?.fields || {} );

		// Load meta keys. Comments meta isn't exposed in the join builder
		// (commentmeta is rarely populated and not part of the supported feature set).
		let metaPath = '';
		if ( join.target_type === 'users' ) {
			metaPath = 'wlconduit/v1/meta-keys?object_type=user';
		} else if ( join.target_type === 'posts' && join.target_post_type ) {
			metaPath = `wlconduit/v1/meta-keys?object_type=post&post_type=${ join.target_post_type }`;
		} else if ( join.target_type === 'posts' ) {
			metaPath = 'wlconduit/v1/meta-keys?object_type=post';
		}

		const metaPromise = metaPath
			? apiFetch( { path: metaPath } )
				.then( ( data ) => setTargetMeta( data || [] ) )
				.catch( () => setTargetMeta( [] ) )
			: Promise.resolve( setTargetMeta( [] ) );

		Promise.all( [ columnsPromise, metaPromise ] )
			.finally( () => setLoadingTarget( false ) );
	}, [ join.target_type, join.target_post_type ] );

	function handleMyFieldChange( value ) {
		if ( ! value ) {
			return;
		}
		const parts = value.split( ':' );
		onChange( {
			local_field_from: parts[ 0 ] === 'self' ? 'self' : parseInt( parts[ 0 ], 10 ),
			local_field: parts[ 1 ] || '',
		} );
	}

	function handleTargetFieldChange( value ) {
		if ( ! value ) {
			return;
		}
		onChange( { target_field: value } );
	}

	function toggleTargetField( field ) {
		const current = join.target_fields || [];
		if ( current.includes( field ) ) {
			onChange( { target_fields: current.filter( ( f ) => f !== field ) } );
		} else {
			onChange( { target_fields: [ ...current, field ] } );
		}
	}

	function toggleTargetMeta( key ) {
		const current = join.target_meta_keys || {};
		if ( current[ key ] ) {
			const updated = { ...current };
			delete updated[ key ];
			onChange( { target_meta_keys: updated } );
		} else {
			onChange( { target_meta_keys: { ...current, [ key ]: { as_field: true } } } );
		}
	}

	// Current values for selects.
	const myFieldValue = join.local_field
		? `${ join.local_field_from === 'self' ? 'self' : join.local_field_from }:${ join.local_field }`
		: '';

	const targetFieldValue = join.target_field || '';

	// Flatten my field options for SelectControl.
	const flatMyOptions = [ { label: '-- Select --', value: '' } ];
	myFieldOptions.forEach( ( group ) => {
		flatMyOptions.push( { label: `── ${ group.label } ──`, value: '', disabled: true } );
		group.options.forEach( ( opt ) => flatMyOptions.push( opt ) );
	} );

	// Their field options — native columns only.
	const theirOptions = [ { label: '-- Select --', value: '' } ];
	Object.entries( targetColumns ).forEach( ( [ col, label ] ) => {
		theirOptions.push( { label: `${ label } (${ col })`, value: col } );
	} );

	return (
		<div className="aic-join-entry">
			<div className="aic-join-header">
				<strong>Join { index + 1 }</strong>
				<Button isSmall isDestructive variant="tertiary" onClick={ onRemove }>
					Remove
				</Button>
			</div>

			<TextControl
				label="Label"
				help="Key name in the response (e.g., 'order', 'customer')"
				value={ join.label || '' }
				onChange={ ( val ) => onChange( { label: val } ) }
			/>

			<SelectControl
				label="Join with"
				value={ join.target_type || 'posts' }
				options={ JOINABLE_BLOCK_TYPES }
				onChange={ ( val ) => onChange( {
					target_type: val,
					target_post_type: val === 'posts' ? 'post' : '',
					target_fields: [],
					target_meta_keys: {},
				} ) }
			/>

			{ join.target_type === 'posts' && (
				<SelectControl
					label="Post Type"
					value={ join.target_post_type || 'post' }
					options={ postTypes.map( ( pt ) => ( { label: pt.internal ? `${ pt.label } (internal)` : pt.label, value: pt.name } ) ) }
					onChange={ ( val ) => onChange( {
						target_post_type: val,
						target_meta_keys: {},
					} ) }
				/>
			) }

			<SelectControl
				label="My field"
				help="Column or meta key from this block or a previous join"
				value={ myFieldValue }
				options={ flatMyOptions }
				onChange={ handleMyFieldChange }
			/>

			{ loadingTarget ? (
				<Spinner />
			) : (
				<SelectControl
					label="Their field"
					help="Column or meta key on the target table"
					value={ targetFieldValue }
					options={ theirOptions }
					onChange={ handleTargetFieldChange }
				/>
			) }

			<SelectControl
				label="Join type"
				value={ join.join_type || 'inner' }
				options={ JOIN_TYPES }
				onChange={ ( val ) => onChange( { join_type: val } ) }
			/>

			<div className="aic-join-target-fields">
				<h4>Fields from joined table</h4>
				{ Object.entries( targetFields ).map( ( [ key, label ] ) => (
					<CheckboxControl
						key={ key }
						label={ label }
						checked={ ( join.target_fields || [] ).includes( key ) }
						onChange={ () => toggleTargetField( key ) }
					/>
				) ) }
			</div>

			{ targetMeta.length > 0 && (
				<div className="aic-join-target-meta">
					<h4>Custom fields from joined table</h4>
					{ targetMeta.map( ( item ) => {
						const key = item.key || item;
						const isInternal = item.internal || false;
						return (
							<CheckboxControl
								key={ key }
								label={ isInternal ? `${ key } (internal)` : key }
								checked={ !! ( join.target_meta_keys || {} )[ key ] }
								onChange={ () => toggleTargetMeta( key ) }
							/>
						);
					} ) }
				</div>
			) }
		</div>
	);
}
