import {__, _n, sprintf} from '@wordpress/i18n';
import {useEffect, useState} from '@wordpress/element';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Spinner from 'react-bootstrap/Spinner';
import Alert from 'react-bootstrap/Alert';
import Table from 'react-bootstrap/Table';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Tooltip from 'react-bootstrap/Tooltip';

/**
 * Views tab — renders per-video statistics from the Infinite Uploads Video
 * service's statistics endpoint.
 *
 * Shape returned by /library/{libraryId}/statistics?videoGuid=<guid>:
 *   {
 *     viewsChart:        { "YYYY-MM-DDTHH:MM:SSZ": number, ... },
 *     watchTimeChart:    { "YYYY-MM-DDTHH:MM:SSZ": number, ... },
 *     countryViewCounts: { "US": number, "GB": number, ... },
 *     engagementScore:   0-1
 *   }
 *
 * The chart is a simple inline SVG bar chart — no chart library dependency
 * for the Views sketch. Matches how the plugin renders the
 * VideoAttributes.jsx summary tiles: readable defaults, no external CSS.
 */
export default function VideoStatistics({video}) {
	const [loading, setLoading] = useState(true);
	const [error, setError] = useState(null);
	const [stats, setStats] = useState(null);

	useEffect(() => {
		if (!video || !video.guid) {
			return;
		}
		fetchStatistics(video.guid);
	}, [video && video.guid]);

	function fetchStatistics(guid) {
		setLoading(true);
		setError(null);
		fetch(
			`${ajaxurl}?action=infinite-uploads-video-statistics&video_id=${encodeURIComponent(
				guid
			)}&nonce=${IUP_VIDEO.nonce}`,
			{method: 'GET', headers: {Accept: 'application/json'}}
		)
			.then((response) => response.json())
			.then((data) => {
				if (data && data.success) {
					setStats(data.data);
				} else {
					setError(
						(data && data.data && data.data.message) ||
							__(
								'Could not load statistics for this video.',
								'infinite-uploads'
							)
					);
				}
				setLoading(false);
			})
			.catch((err) => {
				console.error(err);
				setError(
					__(
						'Could not load statistics for this video.',
						'infinite-uploads'
					)
				);
				setLoading(false);
			});
	}

	if (loading) {
		return (
			<div className="text-center p-5">
				<Spinner animation="border" role="status" size="sm" />{' '}
				<span className="ms-2">
					{__('Loading statistics…', 'infinite-uploads')}
				</span>
			</div>
		);
	}

	if (error) {
		return (
			<Alert variant="warning" className="my-3">
				{error}
			</Alert>
		);
	}

	if (!stats) {
		return null;
	}

	const viewsSeries = seriesFromChart(stats.viewsChart);
	const watchSeries = seriesFromChart(stats.watchTimeChart);
	const totalViews = sumSeries(viewsSeries);
	const totalWatchTime = sumSeries(watchSeries);
	// Bunny returns engagementScore already on a 0-100 scale. The earlier
	// multiplication by 100 was treating it as a 0-1 fraction, so a score
	// of 100 rendered as "10000%". Round to one decimal place.
	const engagement = Math.round(((stats.engagementScore || 0) + Number.EPSILON) * 10) / 10;
	const countries = topCountries(stats.countryViewCounts || {}, 5);

	return (
		<div className="iup-video-statistics">
			<Row className="mb-4">
				<Col md={4}>
					<StatCard
						label={__('Total views', 'infinite-uploads')}
						value={formatNumber(totalViews)}
						icon="dashicons-welcome-view-site"
					/>
				</Col>
				<Col md={4}>
					<StatCard
						label={__('Watch time', 'infinite-uploads')}
						value={formatWatchTime(totalWatchTime)}
						icon="dashicons-clock"
					/>
				</Col>
				<Col md={4}>
					<StatCard
						label={__('Engagement', 'infinite-uploads')}
						value={
							// Bunny returns -1 on videos that don't have enough
							// viewing data for a score yet — treat it the same as
							// a missing field so the tile shows the em-dash
							// instead of literally rendering "-1%".
							stats.engagementScore != null && stats.engagementScore >= 0
								? `${engagement}%`
								: '—'
						}
						icon="dashicons-heart"
						help={__(
							'Indicates how engaging a specific video is on a 0–100 scale based on viewing duration and viewing patterns; higher values reflect stronger viewer retention. Reported at the video level only and may be unavailable when there isn’t enough viewing data.',
							'infinite-uploads'
						)}
					/>
				</Col>
			</Row>

			<Row className="mb-4">
				<Col>
					<h6 className="mb-2">
						{__('Views over time', 'infinite-uploads')}
					</h6>
					{viewsSeries.length ? (
						<BarChart series={viewsSeries} />
					) : (
						<p className="text-muted small">
							{__(
								'No view data yet for this range.',
								'infinite-uploads'
							)}
						</p>
					)}
				</Col>
			</Row>

			<Row>
				<Col md={6}>
					<h6 className="mb-2">
						{__('Top countries', 'infinite-uploads')}
					</h6>
					{countries.length ? (
						<Table size="sm" striped className="mb-0">
							<thead>
								<tr>
									<th>{__('Country', 'infinite-uploads')}</th>
									<th className="text-end">
										{__('Views', 'infinite-uploads')}
									</th>
								</tr>
							</thead>
							<tbody>
								{countries.map((row) => (
									<tr key={row.country}>
										<td>{row.country}</td>
										<td className="text-end">
											{formatNumber(row.views)}
										</td>
									</tr>
								))}
							</tbody>
						</Table>
					) : (
						<p className="text-muted small">
							{__(
								'No country data available yet.',
								'infinite-uploads'
							)}
						</p>
					)}
				</Col>
			</Row>
		</div>
	);
}

// ---------------------------------------------------------------------------
// Presentational helpers.
// ---------------------------------------------------------------------------

function StatCard({label, value, icon, help}) {
	return (
		<div className="p-3 border rounded h-100">
			<div className="text-muted small mb-1 d-flex align-items-center">
				<span className={`dashicons ${icon} me-1`}></span>
				<span>{label}</span>
				{help && (
					<OverlayTrigger
						placement="top"
						overlay={
							<Tooltip
								id={`iup-stat-help-${label}`}
								className="iup-stat-help-tooltip"
							>
								{help}
							</Tooltip>
						}
					>
						<span
							role="button"
							tabIndex={0}
							aria-label={__('What is this?', 'infinite-uploads')}
							className="dashicons dashicons-info-outline ms-1 text-muted"
							style={{
								fontSize: 14,
								width: 14,
								height: 14,
								cursor: 'help',
								lineHeight: 1,
							}}
						></span>
					</OverlayTrigger>
				)}
			</div>
			<div className="fs-4 fw-semibold">{value}</div>
		</div>
	);
}

/**
 * Small SVG bar chart. Zero-dependency; wide-enough to be readable inside
 * the modal. Uses currentColor so it inherits Bootstrap's text color.
 */
function BarChart({series}) {
	const width = 640;
	const height = 140;
	const padding = {top: 8, right: 8, bottom: 18, left: 30};
	const chartW = width - padding.left - padding.right;
	const chartH = height - padding.top - padding.bottom;
	const max = Math.max(1, ...series.map((p) => p.value));
	const barGap = 2;
	const barWidth = Math.max(1, chartW / series.length - barGap);

	return (
		<svg
			viewBox={`0 0 ${width} ${height}`}
			role="img"
			aria-label={__('Views over time chart', 'infinite-uploads')}
			style={{width: '100%', height: 'auto', color: '#0d6efd'}}
		>
			{/* y-axis max label */}
			<text
				x={padding.left - 4}
				y={padding.top + 8}
				fontSize="10"
				textAnchor="end"
				fill="#6c757d"
			>
				{formatNumber(max)}
			</text>
			{/* baseline */}
			<line
				x1={padding.left}
				y1={padding.top + chartH}
				x2={padding.left + chartW}
				y2={padding.top + chartH}
				stroke="#dee2e6"
			/>
			{series.map((point, i) => {
				const h = (point.value / max) * chartH;
				const x = padding.left + i * (barWidth + barGap);
				const y = padding.top + chartH - h;
				return (
					<g key={point.label}>
						<rect
							x={x}
							y={y}
							width={barWidth}
							height={h}
							fill="currentColor"
							opacity={0.85}
						>
							<title>{`${point.label}: ${formatNumber(
								point.value
							)}`}</title>
						</rect>
					</g>
				);
			})}
			{/* first + last date */}
			{series.length > 0 && (
				<>
					<text
						x={padding.left}
						y={height - 4}
						fontSize="10"
						fill="#6c757d"
					>
						{shortDate(series[0].label)}
					</text>
					<text
						x={padding.left + chartW}
						y={height - 4}
						fontSize="10"
						textAnchor="end"
						fill="#6c757d"
					>
						{shortDate(series[series.length - 1].label)}
					</text>
				</>
			)}
		</svg>
	);
}

// ---------------------------------------------------------------------------
// Data helpers.
// ---------------------------------------------------------------------------

/**
 * Turn the endpoint's `{ "ISO-8601": count }` map into an ordered array of
 * { label, value } points, sorted by date ascending.
 */
function seriesFromChart(chart) {
	if (!chart || typeof chart !== 'object') {
		return [];
	}
	return Object.keys(chart)
		.sort()
		.map((label) => ({label, value: Number(chart[label]) || 0}));
}

function sumSeries(series) {
	return series.reduce((n, point) => n + point.value, 0);
}

function topCountries(countryMap, limit) {
	return Object.keys(countryMap)
		.map((country) => ({country, views: Number(countryMap[country]) || 0}))
		.sort((a, b) => b.views - a.views)
		.slice(0, limit);
}

function formatNumber(n) {
	return new Intl.NumberFormat().format(n || 0);
}

function formatWatchTime(seconds) {
	if (!seconds || seconds < 60) {
		return sprintf(
			/* translators: %d: seconds of watch time */
			_n('%d second', '%d seconds', seconds || 0, 'infinite-uploads'),
			seconds || 0
		);
	}
	const minutes = Math.floor(seconds / 60);
	if (minutes < 60) {
		return sprintf(
			/* translators: %d: minutes of watch time */
			_n('%d minute', '%d minutes', minutes, 'infinite-uploads'),
			minutes
		);
	}
	const hours = Math.floor(minutes / 60);
	const remMin = minutes % 60;
	return `${hours}h ${remMin}m`;
}

function shortDate(iso) {
	try {
		const d = new Date(iso);
		// Format against UTC — the API returns daily buckets at 00:00Z and
		// handing that to the local-time formatter rolls back to the previous
		// day for every viewer west of UTC. Buckets are a calendar day in UTC
		// so the label needs to render against the same calendar.
		return d.toLocaleDateString(undefined, {month: 'short', day: 'numeric', timeZone: 'UTC'});
	} catch (e) {
		return iso;
	}
}
