/**
 * Shared helpers for the boxBorder style-schema field type.
 */

import { normalizeLinkMode } from './styleLinkMode';

/**
 * Derive CSS custom-property keys for a boxBorder field.
 *
 * Root from `field.key` (e.g. `--bm-item-border`) yields:
 * `--bm-item-border-width|style|color` and `--bm-item-border-{side}-width`.
 *
 * @param {object} field
 * @return {{
 *   width: string,
 *   style: string,
 *   color: string,
 *   top: string,
 *   right: string,
 *   bottom: string,
 *   left: string,
 *   widthMode: string
 * }}
 */
export function boxBorderKeys( field ) {
	const raw = String( field?.key || '--bm-border' ).trim();
	const root = raw.replace(
		/-(?:width|style|color|top|right|bottom|left)(?:-width)?$/i,
		''
	);
	const base = root || '--bm-border';
	return {
		width: field?.widthKey || `${ base }-width`,
		style: field?.styleKey || `${ base }-style`,
		color: field?.colorKey || `${ base }-color`,
		top: field?.topKey || `${ base }-top-width`,
		right: field?.rightKey || `${ base }-right-width`,
		bottom: field?.bottomKey || `${ base }-bottom-width`,
		left: field?.leftKey || `${ base }-left-width`,
		widthMode: field?.widthModeKey || `${ base }-width-mode`,
	};
}

export const BORDER_STYLE_OPTIONS = [
	{ label: 'Solid', value: 'solid' },
	{ label: 'Dashed', value: 'dashed' },
	{ label: 'Dotted', value: 'dotted' },
	{ label: 'Double', value: 'double' },
	{ label: 'None', value: 'none' },
];

export const DEFAULT_BORDER_WIDTH = '1px';
export const DEFAULT_BORDER_STYLE = 'solid';
export const DEFAULT_BORDER_COLOR = 'rgba(0, 0, 0, 0.1)';

/**
 * Whether a side width value means the side is enabled.
 *
 * @param {string} value
 * @return {boolean}
 */
export function isBorderSideOn( value ) {
	const v = String( value || '' ).trim().toLowerCase();
	if ( ! v || v === '0' || v === '0px' || v === 'none' ) {
		return false;
	}
	return true;
}

/**
 * Normalize side width: on → width, off → 0.
 *
 * @param {boolean} on
 * @param {string}  width
 * @param {string}  [fallback]
 * @return {string}
 */
export function borderSideWidth(
	on,
	width,
	fallback = DEFAULT_BORDER_WIDTH
) {
	if ( ! on ) {
		return '0';
	}
	const w = String( width || '' ).trim();
	if ( ! isBorderSideOn( w ) ) {
		return fallback || DEFAULT_BORDER_WIDTH;
	}
	return w;
}

/**
 * Effective width for a side (ignores off / zero).
 *
 * @param {string} value
 * @param {string} [fallback]
 * @return {string}
 */
export function borderSideEffectiveWidth(
	value,
	fallback = DEFAULT_BORDER_WIDTH
) {
	if ( ! isBorderSideOn( value ) ) {
		return '';
	}
	const w = String( value || '' ).trim();
	return w || fallback;
}

/**
 * Infer width link mode from the four side width values.
 *
 * - All four on + same width → linked (1 field)
 * - Only top/bottom or only left/right (paired) → axial (2 fields)
 * - Exactly one side on, or asymmetric widths → sides (4 fields)
 * - No sides on → linked (shared width ready when sides turn on)
 *
 * @param {{ top: string, right: string, bottom: string, left: string }} sides
 * @return {'linked'|'axial'|'sides'}
 */
export function inferBorderWidthMode( sides ) {
	const top = borderSideEffectiveWidth( sides.top );
	const right = borderSideEffectiveWidth( sides.right );
	const bottom = borderSideEffectiveWidth( sides.bottom );
	const left = borderSideEffectiveWidth( sides.left );

	const onTop = Boolean( top );
	const onRight = Boolean( right );
	const onBottom = Boolean( bottom );
	const onLeft = Boolean( left );
	const onCount =
		( onTop ? 1 : 0 ) +
		( onRight ? 1 : 0 ) +
		( onBottom ? 1 : 0 ) +
		( onLeft ? 1 : 0 );

	if ( onCount === 0 ) {
		return 'linked';
	}

	// One side only → show all four fields so the active side is obvious.
	if ( onCount === 1 ) {
		return 'sides';
	}

	const onlyVertical = ( onTop || onBottom ) && ! onLeft && ! onRight;
	const onlyHorizontal = ( onLeft || onRight ) && ! onTop && ! onBottom;

	// Top↔bottom only (both on, equal) → paired Y/X fields.
	if ( onlyVertical && onTop && onBottom && top === bottom ) {
		return 'axial';
	}
	// Left↔right only (both on, equal) → paired Y/X fields.
	if ( onlyHorizontal && onLeft && onRight && left === right ) {
		return 'axial';
	}

	// All four equal → fully linked.
	if (
		onCount === 4 &&
		top === right &&
		right === bottom &&
		bottom === left
	) {
		return 'linked';
	}

	// Classic axial box: top=bottom, left=right, vertical ≠ horizontal.
	if (
		onCount === 4 &&
		top === bottom &&
		left === right &&
		top !== left
	) {
		return 'axial';
	}

	return 'sides';
}

/**
 * @param {object} values
 * @param {string} key
 * @return {string|null} Trimmed value, or null when the key is absent.
 */
function readStored( values, key ) {
	if ( ! values || ! Object.prototype.hasOwnProperty.call( values, key ) ) {
		return null;
	}
	return String( values[ key ] ?? '' ).trim();
}

/**
 * Resolve border UI state from stored CSS vars (keeps 0 / transparent).
 *
 * @param {object} values
 * @param {ReturnType<typeof boxBorderKeys>} keys
 * @param {object} [field] Optional defaultWidth / defaultColor / defaultStyle / defaultSides
 * @return {{
 *   style: string,
 *   color: string,
 *   masterWidth: string,
 *   sideRaw: { top: string, right: string, bottom: string, left: string },
 *   sidesOn: { top: boolean, right: boolean, bottom: boolean, left: boolean },
 *   sideWidths: { top: string, right: string, bottom: string, left: string },
 *   linkedWidth: string,
 *   axialY: string,
 *   axialX: string,
 *   widthMode: 'linked'|'axial'|'sides'
 * }}
 */
export function resolveBoxBorderState( values = {}, keys, field = {} ) {
	const fallbackWidth = field.defaultWidth ?? DEFAULT_BORDER_WIDTH;
	const fallbackStyle = field.defaultStyle ?? DEFAULT_BORDER_STYLE;
	const fallbackColor = field.defaultColor ?? DEFAULT_BORDER_COLOR;
	const defaultSides = field.defaultSides || null;

	const widthStored = readStored( values, keys.width );
	const styleStored = readStored( values, keys.style );
	const colorStored = readStored( values, keys.color );

	const masterWidth =
		widthStored != null && widthStored !== ''
			? widthStored
			: fallbackWidth;
	const style =
		styleStored != null && styleStored !== ''
			? styleStored
			: fallbackStyle;
	const color =
		colorStored != null ? colorStored || 'transparent' : fallbackColor;

	const sideKeys = [ keys.top, keys.right, keys.bottom, keys.left ];
	const anySideStored = sideKeys.some(
		( k ) => readStored( values, k ) != null
	);

	const pickSide = ( sideKey, sideId ) => {
		const stored = readStored( values, sideKey );
		if ( stored != null ) {
			return stored;
		}
		if ( defaultSides && defaultSides[ sideId ] != null ) {
			return String( defaultSides[ sideId ] );
		}
		if ( anySideStored ) {
			return '0';
		}
		return masterWidth;
	};

	const sideRaw = {
		top: pickSide( keys.top, 'top' ),
		right: pickSide( keys.right, 'right' ),
		bottom: pickSide( keys.bottom, 'bottom' ),
		left: pickSide( keys.left, 'left' ),
	};

	const sidesOn = {
		top: isBorderSideOn( sideRaw.top ),
		right: isBorderSideOn( sideRaw.right ),
		bottom: isBorderSideOn( sideRaw.bottom ),
		left: isBorderSideOn( sideRaw.left ),
	};

	const onWidths = [ 'top', 'right', 'bottom', 'left' ]
		.filter( ( id ) => sidesOn[ id ] )
		.map( ( id ) => sideRaw[ id ] );
	const editWidth =
		onWidths[ 0 ] ||
		( isBorderSideOn( masterWidth ) ? masterWidth : '' ) ||
		fallbackWidth;

	// Display / persist the real side values. Off sides stay 0 — do not
	// mirror the active side's width into empty sides (that looked like
	// "all sides have values" when only bottom was on).
	const sideWidths = {
		top: sidesOn.top ? sideRaw.top : '0',
		right: sidesOn.right ? sideRaw.right : '0',
		bottom: sidesOn.bottom ? sideRaw.bottom : '0',
		left: sidesOn.left ? sideRaw.left : '0',
	};

	const linkedWidth = onWidths[ 0 ] || editWidth;
	// Axis display values: real on-side widths only — never borrow the other axis.
	const axialY =
		( sidesOn.top && sideRaw.top ) ||
		( sidesOn.bottom && sideRaw.bottom ) ||
		'0';
	const axialX =
		( sidesOn.left && sideRaw.left ) ||
		( sidesOn.right && sideRaw.right ) ||
		'0';

	const widthModeStored = normalizeLinkMode(
		readStored( values, keys.widthMode )
	);

	return {
		style,
		color,
		masterWidth,
		sideRaw,
		sidesOn,
		sideWidths,
		/** Width to apply when turning an off side on. */
		draftWidth: editWidth,
		linkedWidth,
		axialY,
		axialX,
		widthMode: widthModeStored || inferBorderWidthMode( sideRaw ),
	};
}
