import { ReactNode, Ref, useState } from 'react';
import { createPortal } from 'react-dom';
import { MapContainer, MapContainerProps, TileLayer, TileLayerProps } from 'react-leaflet';
import {
	FormControl,
	FormControlProps,
	FormHelperText,
	FormHelperTextProps,
	FormLabel,
	FormLabelProps
} from '@mui/material';
import clsx from 'clsx';
import { LatLngLiteral, Map } from 'leaflet';
import { isFunction } from 'lodash';

import useDefaultProps from '../../../hooks/useDefaultProps';
import Controls, { IControlsProps } from '../shared/Controls';
import MapWrapper, { MapWrapperProps } from '../shared/MapWrapper';
import StaticMarker, { StaticMarkerProps } from '../shared/StaticMarker';

import ChangeOnMoveHandler from './ChangeOnMoveHandler';
import FloatingMarker from './FloatingMarker';

export type LocationPickerProps = {
	name?: string;
	required?: boolean;
	error?: boolean;
	label?: ReactNode;
	disabled?: boolean;
	helperText?: React.ReactNode;
	value?: LatLngLiteral;
	onChange?: (value: LatLngLiteral) => void;
	ref?: Ref<Map>;
	inputRef?: Ref<HTMLInputElement>;

	slotProps?: {
		formControl?: Omit<FormControlProps, 'name' | 'disabled' | 'error' | 'children' | 'defaultValue'>;
		formLabel?: Omit<FormLabelProps, 'control' | 'ref' | 'label' | 'required'>;
		helperText?: FormHelperTextProps;
		mapWrapper?: MapWrapperProps;
		tileLayer?: TileLayerProps;
		staticMarker?: Omit<StaticMarkerProps, 'position'>;
		controls?: Omit<IControlsProps, 'onChange'>;
	};

	slots?: {
		floatingMarker?: ReactNode;
	};
} & Omit<MapContainerProps, 'children' | 'center'>;

const defaultPosition: LatLngLiteral = { lat: 35.6997, lng: 51.338 };

const LocationPicker = (inProps: LocationPickerProps) => {
	const {
		required,
		error,
		label,
		zoom = 15,
		helperText,
		value,
		disabled,
		onChange,
		ref,
		name,
		inputRef,
		slotProps,
		className,
		slots,
		...mapContainerProps
	} = useDefaultProps({
		name: 'RemateLocationPicker',
		props: inProps
	});

	const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null);

	const _value = value ?? defaultPosition;

	return (
		<FormControl
			fullWidth
			disabled={disabled}
			{...slotProps?.formControl}
			required={required}
			error={error}
		>
			<input
				style={{ width: 0, height: 0 }}
				name={name}
				ref={inputRef}
			/>

			{label && (
				<FormLabel
					sx={(theme) => ({
						pb: 0.75,
						...(isFunction(slotProps?.formLabel?.sx)
							? (slotProps.formLabel.sx as Function)(theme)
							: slotProps?.formLabel?.sx)
					})}
					{...slotProps?.formLabel}
				>
					{label}
				</FormLabel>
			)}

			<MapWrapper
				{...slotProps?.mapWrapper}
				ref={(_rootElement) => setRootElement(_rootElement)}
				className={clsx(error && 'error')}
			>
				<MapContainer
					fadeAnimation
					zoomAnimation
					markerZoomAnimation
					attributionControl={false}
					zoomControl={false}
					doubleClickZoom
					trackResize
					touchZoom
					boxZoom
					preferCanvas
					bounceAtZoomLimits
					tapHold
					maxZoom={18}
					minZoom={0}
					dragging={true}
					scrollWheelZoom={true}
					center={_value}
					zoom={zoom}
					ref={ref}
					{...mapContainerProps}
					className={clsx('map-container', className)}
				>
					<TileLayer
						attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
						url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
						{...slotProps?.tileLayer}
					/>

					{!disabled && <ChangeOnMoveHandler onChange={onChange} />}

					{disabled ? (
						<StaticMarker
							{...slotProps?.staticMarker}
							position={_value}
						/>
					) : (
						(slots?.floatingMarker ?? <FloatingMarker />)
					)}

					{rootElement &&
						createPortal(
							<Controls
								enableShowUserLocation={!disabled}
								{...slotProps?.controls}
								onChange={onChange}
							/>,
							rootElement
						)}
				</MapContainer>
			</MapWrapper>

			{helperText && <FormHelperText {...slotProps?.helperText}>{helperText}</FormHelperText>}
		</FormControl>
	);
};

declare module '@mui/material' {
	interface Components {
		RemateLocationPicker?: {
			defaultProps?: Partial<LocationPickerProps>;
		};
	}
}

export default LocationPicker;
