import React, { useEffect, useMemo } from "react";
import { __, sprintf } from "@wordpress/i18n";
import { Info } from "lucide-react";
import { BILLING_PERIODS } from "./schedule";

export const RenewalSyncPeriodNotice = ({ schedule, className }) => schedule.mode === "full_flexible" ? (
	<p className={className}>
		{sprintf(
			/* translators: %s: billing period used to configure renewal segments. */
			__("This custom segment plan applies to %s billing. Other billing periods follow your store’s renewal sync settings.", "arraysubs"),
			BILLING_PERIODS.find(({ value }) => value === schedule.period)?.label || schedule.period
		)}
	</p>
) : null;

const MIN_CYCLE_DAYS = 3;

const SEGMENTS = [
	{ id: 1, color: "#2e9e4f", label: __("Full amount", "arraysubs") },
	{ id: 2, color: "var(--wp-admin-theme-color)", label: __("Prorate amount", "arraysubs") },
	{ id: 3, color: "#d63638", label: __("Charge full for next billing cycle", "arraysubs") },
];

/** Nominal cycle length, matching SegmentPlan::getNominalCycleDays(). */
export const nominalCycleDays = (period, interval) => {
	const perPeriod = { day: 1, week: 7, month: 30, year: 365 };
	return (perPeriod[period] || 30) * Math.max(1, Number(interval) || 1);
};

const defaultThirds = (days) => {
	const b1 = Math.max(1, Math.round(days / 3));
	const b2 = Math.max(b1 + 1, Math.round((days * 2) / 3));
	return [b1, Math.min(b2, days - 1)];
};

/** Match RenewalSegmentPlan's boundary sanitizers before displaying or saving. */
export const normalizeRenewalSync = (sync, schedule) => {
	const days = Math.max(MIN_CYCLE_DAYS, nominalCycleDays(schedule.period, schedule.interval));
	const defaults = defaultThirds(days);
	const first = Math.max(0, parseInt(sync.seg1_end, 10) || 0);
	const second = Math.max(0, parseInt(sync.seg2_end, 10) || 0);
	let b1 = first;
	let b2 = second;
	if (b1 < 1 || b2 <= b1 || b2 >= days) {
		b1 = Math.max(1, Math.min(b1 || defaults[0], days - 2));
		b2 = Math.max(b1 + 1, Math.min(b2 || defaults[1], days - 1));
	}
	const activeCount = SEGMENTS.filter(({ id }) => sync[`seg${id}_active`]).length;
	if (activeCount === 2) {
		b1 = first >= 1 && first < days ? first : Math.max(1, Math.round(days / 2));
	}
	return sync.seg1_end === b1 && sync.seg2_end === b2
		? sync : { ...sync, seg1_end: b1, seg2_end: b2 };
};

/**
 * "Flexible Renewal Sync" step — the box equivalent of the segment plan on
 * simple products. Active segments split the billing cycle and decide how the
 * first payment is charged based on the signup day.
 *
 * The box owns this setting outright: the subscription the customer buys is the
 * box, so the segment plan is read from the box product. Leaving it off does not
 * disable syncing — it hands the box back to the store-wide renewal sync setting
 * (Settings → General → Sync Renewals to Next Billing Cycle). Either way, the
 * renewal-sync settings on the products
 * placed inside the box are never consulted: their child subscriptions inherit
 * the box's dates on every renewal.
 */
const SyncRenewalsBuilder = ({ sync, schedule, onChange, productContext = false }) => {
	const cycleDays = nominalCycleDays(schedule.period, schedule.interval);
	const sliderDays = Math.max(MIN_CYCLE_DAYS, cycleDays);
	const tooShort = cycleDays < MIN_CYCLE_DAYS;
	const normalized = normalizeRenewalSync(sync, schedule);
	useEffect(() => {
		if (sync.enabled && normalized !== sync) {
			onChange(normalized);
		}
	}, [sync, normalized, onChange]);

	const actives = useMemo(
		() => SEGMENTS.filter((s) => sync[`seg${s.id}_active`]).map((s) => s.id),
		[sync]
	);

	const boundaries = useMemo(() => {
		if (actives.length === 3) {
			return [normalized.seg1_end, normalized.seg2_end];
		}
		if (actives.length === 2) {
			return [normalized.seg1_end];
		}
		return [];
	}, [normalized.seg1_end, normalized.seg2_end, actives.length]);

	const update = (patch) => onChange({ ...normalized, ...patch });

	const setBoundary = (index, rawValue) => {
		const step = 1;
		const lower = index > 0 ? boundaries[index - 1] + step : 1;
		const upper = index < boundaries.length - 1 ? boundaries[index + 1] - step : sliderDays - 1;
		const value = Math.round(Math.min(Math.max(Number(rawValue), lower), Math.max(lower, upper)));
		if (index === 0) {
			update({ seg1_end: value, seg2_end: boundaries[1] || normalized.seg2_end });
		} else {
			update({ seg2_end: value });
		}
	};

	const toggleSegment = (id) => {
		const key = `seg${id}_active`;
		const nextActive = !sync[key];
		// At least one segment must stay on.
		if (!nextActive && actives.length === 1) {
			return;
		}
		const next = { ...normalized, [key]: nextActive };
		// Redistribute evenly for the new active count.
		const count = SEGMENTS.filter((s) => next[`seg${s.id}_active`]).length;
		if (count === 3) {
			const [d1, d2] = defaultThirds(sliderDays);
			next.seg1_end = d1;
			next.seg2_end = d2;
		} else if (count === 2) {
			next.seg1_end = Math.max(1, Math.round(sliderDays / 2));
		}
		onChange(next);
	};

	// Day ranges per active segment for the legend.
	const ranges = useMemo(() => {
		const map = {};
		let from = 1;
		actives.forEach((id, i) => {
			const to = i < boundaries.length ? boundaries[i] : sliderDays;
			map[id] = from >= to ? `${to}` : `${from} - ${to}`;
			from = to + 1;
		});
		return map;
	}, [actives, boundaries, sliderDays]);

	const gradient = useMemo(() => {
		if (actives.length === 0) {
			return "#c3c4c7";
		}
		const stops = [];
		let prevPct = 0;
		actives.forEach((id, i) => {
			const to = i < boundaries.length ? boundaries[i] : sliderDays;
			const pct = Math.min(100, (to / sliderDays) * 100);
			const color = SEGMENTS.find((s) => s.id === id).color;
			stops.push(`${color} ${prevPct}% ${pct}%`);
			prevPct = pct;
		});
		return `linear-gradient(to right, ${stops.join(", ")})`;
	}, [actives, boundaries, sliderDays]);

	return (
		<div className="arraysubs-box-sync">
			<div className="arraysubs-box-section">
				<div className="arraysubs-box-section__head">
					<h3 className="arraysubs-box-section__title">
						{__("Flexible Renewal Sync", "arraysubs")}
					</h3>
					<p className="arraysubs-box-section__desc">
						{productContext ? __("Align renewals to calendar boundaries and choose the first payment for each signup segment.", "arraysubs") : __(
							"Align every box renewal to the same calendar boundary and choose how the first payment is charged, based on which day of the cycle the customer subscribes.",
							"arraysubs"
						)}
					</p>
				</div>

				{!productContext && <div className="arraysubs-box-alert">
					<Info size={16} className="arraysubs-box-alert__icon" />
					<div className="arraysubs-box-alert__body">
						<strong>
							{__(
								"Renewal sync for this box is always decided here, never by the products inside it.",
								"arraysubs"
							)}
						</strong>
						<ul>
							<li>
								{__(
									"Leave this unchecked and the box follows your store-wide renewal sync setting (Settings → General → Sync Renewals to Next Billing Cycle), exactly like any other subscription product.",
									"arraysubs"
								)}
							</li>
							<li>
								{__(
									"Tick it to give this box its own segment plan. It then applies whether or not store-wide renewal sync is turned on.",
									"arraysubs"
								)}
							</li>
							<li>
								{__(
									"Renewal sync settings on the products added to this box are ignored. The customer buys one subscription — the box — and each included subscription product simply inherits the box's renewal dates.",
									"arraysubs"
								)}
							</li>
						</ul>
					</div>
				</div>}

				<RenewalSyncPeriodNotice schedule={schedule} className="arraysubs-box-section__desc" />

				<label className="arraysubs-box-checkbox">
					<input
						type="checkbox"
						checked={!!sync.enabled}
						onChange={(event) => {
							const enabled = event.target.checked;
							// Seed the boundaries on enable so the saved config
							// carries the same values the slider displays.
							if (enabled && !(sync.seg1_end > 0 && sync.seg2_end > 0)) {
								const [d1, d2] = defaultThirds(sliderDays);
								update({ enabled, seg1_end: d1, seg2_end: d2 });
								return;
							}
							update({ enabled });
						}}
					/>
					<span>
						<strong>
							{productContext ? __("Use a custom renewal segment plan", "arraysubs") : __("Give this box its own segment plan", "arraysubs")}
						</strong>
						<span className="arraysubs-box-field__help">
							{__(
								"Leave unchecked to follow the store-wide renewal sync setting. Supported for manual payment gateways and Stripe.",
								"arraysubs"
							)}
						</span>
					</span>
				</label>

				{sync.enabled && tooShort && (
					<div className="arraysubs-box-alert arraysubs-box-alert--warning">
						<Info size={16} className="arraysubs-box-alert__icon" />
						<div className="arraysubs-box-alert__body">
							{__(
								"This billing cycle is too short to split into segments. Renewal sync needs a cycle of at least 3 days.",
								"arraysubs"
							)}
						</div>
					</div>
				)}

				{sync.enabled && !tooShort && (
					<>
						<div className="arraysubs-box-sync__slider">
							<div className="arraysubs-box-sync__bar" style={{ background: gradient }}>
								{boundaries.map((boundary, index) => (
									// eslint-disable-next-line react/no-array-index-key
									<React.Fragment key={index}>
										<input
											type="range"
											className="arraysubs-box-sync__range"
											min={0}
											max={sliderDays}
											step={1}
											value={boundary}
											onChange={(event) => setBoundary(index, event.target.value)}
											aria-label={sprintf(
												/* translators: %d: boundary number. */
												__("End day of active segment %d", "arraysubs"),
												index + 1
											)}
										/>
										<span
											className="arraysubs-box-sync__bubble"
											style={{
												left: `calc(${Math.min(100, (boundary / sliderDays) * 100)}% - ${
													(Math.min(100, (boundary / sliderDays) * 100) / 100) * 18 - 9
												}px)`,
											}}
										>
											{sprintf(
												/* translators: %d: day of the billing cycle. */
												__("Day %d", "arraysubs"),
												boundary
											)}
										</span>
									</React.Fragment>
								))}
							</div>
							<div className="arraysubs-box-sync__scale">
								<span>{__("Day 1", "arraysubs")}</span>
								<span>
									{sprintf(
										/* translators: %d: last day of the billing cycle. */
										__("Day %d", "arraysubs"),
										sliderDays
									)}
								</span>
							</div>
						</div>

						<div className="arraysubs-box-sync__legend">
							{SEGMENTS.map((segment) => {
								const isActive = !!sync[`seg${segment.id}_active`];
								return (
									<div
										key={segment.id}
										className={`arraysubs-box-sync__legend-row${isActive ? "" : " is-inactive"}`}
									>
										<label className="arraysubs-box-switch">
											<input
												type="checkbox"
												checked={isActive}
												onChange={() => toggleSegment(segment.id)}
											/>
											<span
												className="arraysubs-box-switch__track"
												style={isActive ? { background: segment.color } : undefined}
											/>
										</label>
										<span className="arraysubs-box-sync__range-label">
											{isActive ? ranges[segment.id] : __("Off", "arraysubs")}
										</span>
										<span
											className="arraysubs-box-sync__seg-label"
											style={{ color: isActive ? segment.color : undefined }}
										>
											{segment.label}
										</span>
									</div>
								);
							})}
						</div>

						<p className="arraysubs-box-section__desc">
							{__(
								"Active segments split the billing cycle between them; disabling a segment lets the remaining ones cover its days. Cycles align to calendar boundaries (months start on the 1st, weeks on the store's start-of-week day, years on January 1) and extra calendar days go to the last active segment.",
								"arraysubs"
							)}
						</p>
					</>
				)}
			</div>
		</div>
	);
};

export default SyncRenewalsBuilder;
