import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Ability } from "~/components/Ability";
import type { MainWeaponId } from "~/modules/in-game-lists/types";
import { SendouButton } from "../../../components/elements/Button";
import { SendouPopover } from "../../../components/elements/Popover";
import { MAX_AP } from "../analyzer-constants";
import type { FullInkTankOption, SpecialWeaponParams } from "../analyzer-types";
import { fullInkTankOptions } from "../core/stats";
import { mainWeaponParams, weaponParams } from "../core/utils";
import styles from "../routes/analyzer.module.css";

interface PerInkTankGridProps {
	weaponSplId: MainWeaponId;
}

export function PerInkTankGrid(props: PerInkTankGridProps) {
	const { t } = useTranslation(["analyzer"]);

	return (
		<SendouPopover
			popoverClassName={styles.inkGridContainer}
			trigger={
				<SendouButton variant="minimal" size="small">
					{t("analyzer:button.showConsumptionGrid")}
				</SendouButton>
			}
		>
			<Grid {...props} />
		</SendouPopover>
	);
}

// if adding to this, also update analyzer.css
const AP_VALUES_TO_SHOW = [
	0, 3, 6, 9, 10, 12, 13, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
	29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 41, 42, 44, 45, 47, 48, 51, 54,
	57,
];

function Grid({ weaponSplId }: PerInkTankGridProps) {
	const { t } = useTranslation(["weapons", "analyzer"]);
	const [subsUsed, setSubsUsed] = React.useState(1);
	const [ismHovered, setIsmHovered] = React.useState<number>();
	const [issHovered, setIssHovered] = React.useState<number>();

	const values = React.useMemo(
		() => calculateGrid({ weaponSplId, subsUsed }),
		[weaponSplId, subsUsed],
	);

	const handleHover = ({ ismAP, issAP }: { ismAP: number; issAP: number }) => {
		setIsmHovered(ismAP);
		setIssHovered(issAP);
	};

	const handleMouseLeaveGrid = () => {
		setIsmHovered(undefined);
		setIssHovered(undefined);
	};

	return (
		<div>
			<h2 className="text-lg text-center">
				{t("analyzer:perInkTankGrid.header", {
					weapon: t(`weapons:MAIN_${weaponSplId}`),
					count: subsUsed,
				})}
			</h2>
			<div className="stack horizontal md justify-center my-2">
				{[0, 1, 2].map((subsUsedOption) => {
					const id = String(subsUsedOption);
					return (
						<div
							key={subsUsedOption}
							className="stack horizontal xs items-center"
						>
							<input
								type="radio"
								id={id}
								name="subsUsed"
								value={subsUsedOption}
								checked={subsUsedOption === subsUsed}
								onChange={(e) => setSubsUsed(Number(e.target.value))}
							/>
							<label htmlFor={id} className="mb-0">
								{subsUsedOption}
							</label>
						</div>
					);
				})}
			</div>
			{/** biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration */}
			<div className="stack horizontal sm" onMouseLeave={handleMouseLeaveGrid}>
				<div className={styles.inkGridHorizontalAbility}>
					<Ability ability="ISS" size="SUBTINY" />
				</div>
				<div className={styles.inkGrid}>
					<div className={styles.inkGridHorizontalAbility}>
						<Ability ability="ISM" size="SUBTINY" />
					</div>
					<div />
					{AP_VALUES_TO_SHOW.map((ap) => (
						<div
							className={clsx(
								styles.inkGridAp,
								ismHovered === ap && styles.inkGridApFocused,
							)}
							key={ap}
						>
							{ap}
						</div>
					))}
					{values.map((row, i) =>
						[
							<div
								className={clsx(
									styles.inkGridAp,
									issHovered === AP_VALUES_TO_SHOW[i] &&
										styles.inkGridApFocused,
								)}
								key={i}
							>
								{AP_VALUES_TO_SHOW[i]}
							</div>,
						].concat(
							row.map((cell, j) => {
								const key = `${i}-${j}`;

								if (cell === "N/A") {
									return <div className={styles.inkGridCell} key={key} />;
								}

								const title = `${cell.shots ?? "-"} (ISM: ${cell.ismAP}, ISS: ${
									cell.issAP
								})`;

								if (typeof cell.shots !== "number") {
									return (
										// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
										<div
											className={styles.inkGridCell}
											key={key}
											style={{ "--cell-color": "var(--color-bg-high)" }}
											title={title}
											onMouseEnter={() =>
												handleHover({ ismAP: cell.ismAP, issAP: cell.issAP })
											}
										>
											-
										</div>
									);
								}

								return (
									// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
									<div
										key={key}
										className={styles.inkGridCell}
										style={{ "--cell-color": cell.hex }}
										title={title}
										onMouseEnter={() =>
											handleHover({ ismAP: cell.ismAP, issAP: cell.issAP })
										}
									>
										{cell.shots}
									</div>
								);
							}),
						),
					)}
				</div>
			</div>
		</div>
	);
}

type ShotCellData = { shots: number | null; ismAP: number; issAP: number };

// LDE boosts both ISM and ISS by max 18 AP each, but you need 10 AP to wear it.
const MAX_LDE_AP = 18 * 2;
const AP_NEEDED_TO_WEAR_LDE = 10;
const apsArePossible = (issAP: number, ismAP: number) =>
	issAP + ismAP - MAX_LDE_AP + AP_NEEDED_TO_WEAR_LDE <= MAX_AP;
function calculateGrid({
	weaponSplId,
	subsUsed,
}: {
	weaponSplId: MainWeaponId;
	subsUsed: number;
}) {
	const result: ("N/A" | ShotCellData)[][] = [];
	for (
		let issAPIndex = 0;
		issAPIndex < AP_VALUES_TO_SHOW.length;
		issAPIndex++
	) {
		const issAP = AP_VALUES_TO_SHOW[issAPIndex];

		const row: ("N/A" | ShotCellData)[] = [];
		for (
			let ismAPIndex = 0;
			ismAPIndex < AP_VALUES_TO_SHOW.length;
			ismAPIndex++
		) {
			const ismAP = AP_VALUES_TO_SHOW[ismAPIndex];

			if (!apsArePossible(issAP, ismAP)) {
				row.push("N/A" as const);
				continue;
			}

			const option = inkTankOptionsWhenNSubsUsed({
				ismAP,
				issAP,
				subsUsed,
				weaponSplId,
			});

			row.push({
				shots: option?.value ? Math.floor(option.value) : null,
				issAP,
				ismAP,
			});
		}

		result.push(row);
	}

	const withColors = addGridColors(result);

	return withColors;
}

// this is a performance optimization over simply calling "buildStats"
// as it would be doing a lot of unnecessary work
function inkTankOptionsWhenNSubsUsed({
	issAP,
	ismAP,
	subsUsed,
	weaponSplId,
}: {
	issAP: number;
	ismAP: number;
	subsUsed: number;
	weaponSplId: MainWeaponId;
}) {
	const mainParams = mainWeaponParams(weaponSplId);

	const subWeaponParams = weaponParams().subWeapons[mainParams.subWeaponId];

	const specialWeaponParams = weaponParams().specialWeapons[
		mainParams.specialWeaponId
	] as SpecialWeaponParams;

	const options = fullInkTankOptions({
		abilityPoints: new Map([
			["ISS", issAP],
			["ISM", ismAP],
		]),
		weaponSplId,
		hasTacticooler: false,
		mainOnlyAbilities: [],
		mainWeaponParams: mainParams,
		specialWeaponParams,
		subWeaponParams,
	});

	return optionsToPreferred(options).find((o) => o.subsUsed === subsUsed);
}

const PREFERRED_KEYS: FullInkTankOption["type"][] = ["FULL_CHARGE"];
function optionsToPreferred(options: ReturnType<typeof fullInkTankOptions>) {
	if (!options.some((option) => PREFERRED_KEYS.includes(option.type))) {
		return options;
	}

	return options.filter((option) => PREFERRED_KEYS.includes(option.type));
}

function addGridColors(grid: ("N/A" | ShotCellData)[][]) {
	const maxValue = grid
		.flat()
		.filter(
			(v): v is { shots: number; ismAP: number; issAP: number } =>
				v !== "N/A" && v.shots !== null,
		)
		.reduce((max, v) => Math.max(max, v.shots), 0);

	const minValue = grid
		.flat()
		.filter(
			(v): v is { shots: number; ismAP: number; issAP: number } =>
				v !== "N/A" && v.shots !== null,
		)
		.reduce((min, v) => Math.min(min, v.shots), Number.POSITIVE_INFINITY);

	const result = grid.map((row) =>
		row.map((cell) => {
			if (cell === "N/A") return cell;
			if (cell.shots === null) return { ...cell, hex: undefined };

			const { shots } = cell;
			const hex = generateHexCode({
				minValue,
				maxValue,
				value: shots,
			});

			return {
				...cell,
				hex,
			};
		}),
	);

	return result;
}

function generateHexCode({
	minValue,
	maxValue,
	value,
}: {
	minValue: number;
	maxValue: number;
	value: number;
}) {
	const clampedValue = Math.max(minValue, Math.min(value, maxValue));
	const normalizedPosition = (clampedValue - minValue) / (maxValue - minValue);

	// Adjust saturation and brightness to lighten the colors
	const saturationFactor = 0.5; // Adjust this to control the saturation
	const brightnessFactor = 0.8; // Adjust this to control the brightness

	const red =
		Math.floor(255 * (1 - normalizedPosition) * saturationFactor) +
		Math.floor(255 * (1 - brightnessFactor));
	const green =
		Math.floor(255 * normalizedPosition * saturationFactor) +
		Math.floor(255 * (1 - brightnessFactor));
	const blue = Math.floor(255 * (1 - brightnessFactor));

	const hex = ((1 << 24) | (red << 16) | (green << 8) | blue)
		.toString(16)
		.slice(1)
		.toUpperCase();

	return `#${hex}`;
}
