@use "sass:map";
@use "sass:string";
@use "@db-ui/foundations/build/styles/icons";
@use "@db-ui/foundations/build/styles/fonts";
@use "@db-ui/foundations/build/styles/variables";
@use "@db-ui/foundations/build/styles/colors";
@use "@db-ui/foundations/build/styles/helpers";
@use "component";

@forward "../visually-hidden";

$dropdown-icon-transition: transform variables.$db-transition-straight-emotional;
$dropdown-icon-transform: rotate(-180deg);

$font-size-height: calc(var(--db-base-font-size) * var(--db-base-line-height));
$icon-size-sm: var(--db-base-body-icon-font-size-sm);
$label-size-height-sm: calc(
	var(--db-type-body-font-size-sm) * var(--db-type-body-line-height-sm)
);
$label-size-height-xs: calc(
	var(--db-type-body-font-size-xs) * var(--db-type-body-line-height-xs)
);
$label-size-height-2xs: calc(
	var(--db-type-body-font-size-2xs) * var(--db-type-body-line-height-2xs)
);

$floating-label-size-calc: calc(
	#{variables.$db-spacing-fixed-3xs} + #{variables.$db-spacing-fixed-2xs} +
		#{$label-size-height-2xs}
);

$input-types: "button", "checkbox", "color", "date", "datetime-local", "email",
	"file", "hidden", "image", "month", "number", "password", "radio", "range",
	"reset", "search", "submit", "tel", "text", "time", "url", "week";

$check-border-size: min(#{variables.$db-border-height-2xs}, 2px);

%dropdown-icon {
	@include icons.set-icon("chevron_down", "after");

	&::after {
		pointer-events: none;

		@media screen and (prefers-reduced-motion: no-preference) {
			transition: $dropdown-icon-transition;
		}
	}
}

%helper-message {
	.db-infotext {
		margin-block-start: variables.$db-spacing-fixed-2xs;

		&[data-semantic] {
			display: none;
		}
	}
}

@mixin placeholder-content() {
	&::placeholder,
	+ [id$="-placeholder"] {
		@content;
	}
}

@mixin set-floating-label-overwrites($selector) {
	&[data-variant="floating"] {
		label {
			@extend %db-overwrite-font-size-md;

			opacity: component.$placeholder-disabled;
			position: absolute;
			z-index: 2;
			inset-block-start: calc(
				(#{variables.$db-sizing-md} - #{$font-size-height}) / 2
			);
			inset-inline: var(
				--db-form-component-padding-inline-start,
				#{variables.$db-spacing-fixed-sm}
			);
			pointer-events: none;
		}

		// icons
		&::after,
		&::before {
			inset-block-start: calc(
				#{variables.$db-sizing-md} / 2 - #{$font-size-height} / 2
			);
		}

		[id$="-placeholder"] {
			@extend %db-overwrite-font-size-sm;

			inset-block-start: calc(
				50% - 0.5em + #{variables.$db-spacing-fixed-3xs}
			);
		}

		#{$selector} {
			@extend %db-overwrite-font-size-sm;

			@include placeholder-content() {
				transition: none;
				opacity: 0;
			}

			@if ($selector == textarea) {
				padding-block-start: $floating-label-size-calc;
				/* stylelint-disable-next-line at-rule-empty-line-before */
			} @else {
				// font-size label which is 2xs + smallest spacing
				padding-block-start: calc(
					var(--db-type-body-font-size-2xs) +
						#{variables.$db-spacing-fixed-3xs}
				);
			}
		}

		&:has(
				#{$selector}:focus-within,
				#{$selector}:is(input, textarea):not(:placeholder-shown),
				> select option:checked:not([hidden])
			) {
			label {
				@extend %db-overwrite-font-size-2xs;

				inset-block-start: variables.$db-spacing-fixed-2xs;
				opacity: 1;
			}

			#{$selector} {
				@include placeholder-content() {
					@media screen and (prefers-reduced-motion: no-preference) {
						transition: opacity
							#{variables.$db-transition-straight-emotional};
					}

					opacity: component.$placeholder-disabled;
				}
			}
		}
	}
}

@mixin get-validity-message($key: "valid") {
	.db-infotext {
		@if ($key == "valid") {
			&[data-semantic="successful"] {
				display: flex;
			}
			/* stylelint-disable-next-line at-rule-empty-line-before */
		} @else {
			&[data-semantic="critical"] {
				display: flex;
			}
		}

		&:not([data-semantic]) {
			display: none;
		}
	}
}

@mixin get-validity-color($selector, $key: "valid") {
	$variant: "successful";

	@if ($key != "valid") {
		$variant: "critical";
	}

	@include get-validity-message($key);

	#{$selector},
	[id$="-placeholder"] {
		color: var(--db-#{$variant}-on-bg-basic-emphasis-100-default);
		caret-color: var(--db-#{$variant}-on-bg-basic-emphasis-100-default);
	}

	@if ($selector != textarea) {
		@extend %db-#{$variant}-variables;
		/* stylelint-disable-next-line at-rule-empty-line-before */
	} @else {
		// this is a workaround for the textarea scrollbar
		--db-textarea-read-only: var(--db-#{$variant}-bg-basic-level-1-default);
		--db-adaptive-bg-basic-transparent-full-default: var(
			--db-#{$variant}-bg-basic-transparent-full-default
		);
		--db-adaptive-bg-basic-transparent-semi-default: var(
			--db-#{$variant}-bg-basic-transparent-semi-default
		);
		--db-adaptive-bg-basic-transparent-hovered: var(
			--db-#{$variant}-bg-basic-transparent-hovered
		);
		--db-adaptive-bg-basic-transparent-pressed: var(
			--db-#{$variant}-bg-basic-transparent-pressed
		);
		--db-adaptive-default: var(
			--db-#{$variant}-on-bg-basic-emphasis-100-default
		);
		--db-adaptive-on-bg-basic-emphasis-60-default: var(
			--db-#{$variant}-on-bg-basic-emphasis-60-default
		);
	}
}

// This doesn't contain text, search and password, because they don't have an auto-validation
$input-valid-types: "color", "date", "datetime-local", "email", "file",
	"hidden", "month", "number", "range", "tel", "time", "url", "week";

@function get-validations($selector, $key) {
	$validations: ":required";

	@if ($selector == input) {
		// https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation
		$validations: ":required, [minlength], [maxlength], [pattern]";

		@each $type in $input-valid-types {
			$validations: $validations + ", [type=#{$type}]";
		}
		/* stylelint-disable-next-line at-rule-empty-line-before */
	} @else if($selector == textarea) {
		$validations: ":required, [minlength], [maxlength]";
	}

	$has-selectors: "&:has(#{$selector}:not([data-custom-validity]):is(#{$validations}):user-#{$key}),";

	@return string.unquote(string.slice($has-selectors, 1, -2));
}

@mixin get-validity($selector, $key: "valid") {
	@if ($key == "valid") {
		$boolean: "false";
	}

	$user: "";

	@if ($selector == check) {
		$user: "user-";
	}

	@if ($selector == check or $selector == radio) {
		$selector: input;

		&:has(
				#{$selector}:not(
						[data-custom-validity]
					):required:#{$user}#{$key}
			) {
			@content;
		}
		/* stylelint-disable-next-line at-rule-empty-line-before */
	} @else {
		#{get-validations($selector,$key)} {
			@content;
		}
	}

	// If data-custom-validity is set we overwrite the selectors above
	&:has(#{$selector}[data-custom-validity="#{$key}"]) {
		@content;
	}
}

@mixin set-required-label($selector) {
	&:has(#{$selector}:required) {
		&:is(label),
		label {
			&::after {
				@include icons.icon-content("*");

				// We're using 1px instead of a token here on purpose, as this is an edge case
				/* stylelint-disable-next-line db-ui/use-spacings */
				padding-inline-start: 1px;
			}
		}
	}
}

@mixin set-default-form-component($selector) {
	--db-form-has-before: 0;

	@extend %db-overwrite-font-size-sm;
	@extend %helper-message;

	@include set-floating-label-overwrites($selector);
	@include set-required-label($selector);

	// we use absolute icons
	position: relative;
	display: flex;
	flex-direction: column;

	@include get-validity($selector) {
		@include get-validity-color($selector, "valid");
	}

	@include get-validity($selector, "invalid") {
		@include get-validity-color($selector, "invalid");
	}

	#{$selector} {
		@extend %db-overwrite-font-size-md;
		@extend %default-interactive-component;
		@extend %form-component-border;

		background-color: colors.$db-adaptive-bg-basic-transparent-semi-default;
		caret-color: colors.$db-adaptive-on-bg-basic-emphasis-100-default;
		appearance: none;
		max-inline-size: 100%;
		inline-size: 100%;

		/* stylelint-disable-next-line db-ui/use-spacings */
		padding-inline: var(
				--db-form-component-padding-inline-start,
				#{variables.$db-spacing-fixed-sm}
			)
			var(
				--db-form-component-padding-inline-end,
				#{variables.$db-spacing-fixed-sm}
			);

		@include placeholder-content() {
			opacity: component.$placeholder-disabled;
		}

		@include helpers.hover {
			background-color: colors.$db-adaptive-bg-basic-transparent-hovered;
		}

		&:is(input, textarea):not(:disabled):read-only {
			background-color: var(
				--db-textarea-read-only,
				#{colors.$db-adaptive-bg-basic-level-1-default}
			) !important;
		}
	}

	// label
	label {
		@extend %db-overwrite-font-size-xs;

		padding-block-end: variables.$db-spacing-fixed-xs;
		max-inline-size: 25ch;
		text-overflow: ellipsis;
		white-space: nowrap;
		overflow: hidden;

		@media screen and (prefers-reduced-motion: no-preference) {
			transition: font-size variables.$db-transition-straight-emotional;
		}
	}

	&[data-hide-label="true"] {
		label {
			@extend %a11y-visually-hidden;

			padding: 0;
		}
	}

	// disabled
	&:has(#{$selector}:disabled) {
		// Decided against cursor: not-allowed, compare to e.g. https://phabricator.wikimedia.org/T121960
		opacity: component.$default-disabled;
		pointer-events: none;
	}

	// icons
	&::after,
	&::before {
		position: absolute;
		pointer-events: none;
		// 1px for border
		inset-block-start: calc(
			#{variables.$db-sizing-md} / 2 - #{variables.$db-spacing-fixed-xs} *
				var(--db-label-visible-spacing, 0) + #{$label-size-height-xs} *
				var(--db-label-visible-height, 1) - 1px
		);
		color: colors.$db-adaptive-on-bg-basic-emphasis-100-default;
	}

	&::before {
		margin-inline-end: variables.$db-spacing-fixed-sm;
		inset-inline-start: variables.$db-spacing-fixed-sm;
	}

	&::after {
		margin-inline-start: variables.$db-spacing-fixed-sm;
		inset-inline-end: variables.$db-spacing-fixed-sm;
	}

	@if ($selector == input or $selector == select) {
		#{$selector} {
			block-size: variables.$db-sizing-md;
			text-overflow: ellipsis;
		}
	}
}

// CHECKBOX & RADIO

@mixin get-validity-color-check($key: "valid") {
	$variant: successful;

	@if ($key != "valid") {
		$variant: critical;
	}

	@include get-validity-message($key);

	input {
		--db-adaptive-bg-basic-transparent-semi: var(
			--db-#{$variant}-bg-basic-transparent-semi-default
		);
		--db-adaptive-bg-basic-transparent-hovered: var(
			--db-#{$variant}-bg-basic-transparent-hovered
		);
		--db-adaptive-bg-basic-transparent-pressed: var(
			--db-#{$variant}-bg-basic-transparent-pressed
		);

		&:not(:checked),
		&:is([type="radio"]) {
			--db-check-element-border-color: var(
				--db-#{$variant}-on-bg-basic-emphasis-70-default
			);
		}

		&:not([type="radio"]):checked {
			@include colors.set-current-colors(
				colors.$db-successful-bg-inverted-contrast-high-default,
				colors.$db-successful-on-bg-inverted-default,
				colors.$db-successful-bg-inverted-contrast-high-hovered,
				colors.$db-successful-bg-inverted-contrast-high-pressed
			);
		}
	}

	&:is(label),
	label {
		color: var(--db-#{$variant}-on-bg-basic-emphasis-80-default);
	}
}

@mixin set-default-check-element($selector) {
	@extend %db-overwrite-font-size-md;

	@include set-required-label(input);

	@include get-validity($selector) {
		@include get-validity-color-check("valid");
	}

	@include get-validity($selector, "invalid") {
		@include get-validity-color-check("invalid");
	}

	&:has(input:disabled) {
		opacity: component.$default-disabled;
	}

	&:is(label),
	label {
		display: flex;
		align-items: flex-start;
		position: relative;
		gap: variables.$db-spacing-fixed-xs;

		&:not(:has(input:disabled)) {
			@include helpers.hover;
			@include helpers.active;
		}
	}

	input {
		background-color: colors.$db-adaptive-bg-basic-transparent-full-default;
		place-content: center center;
		appearance: none;
		aspect-ratio: 1;
		/* stylelint-disable-next-line db-ui/use-border-height */
		border: #{$check-border-size} solid
			var(
				--db-check-element-border-color,
				#{colors.$db-adaptive-on-bg-basic-emphasis-100-default}
			);
		block-size: $font-size-height;
		inline-size: $font-size-height;
		padding: 0;

		@include helpers.hover {
			background-color: colors.$db-adaptive-bg-basic-transparent-hovered;
		}

		@include helpers.active {
			background-color: colors.$db-adaptive-bg-basic-transparent-pressed;
		}
	}

	&[data-size="small"] {
		@extend %db-overwrite-font-size-sm;

		input {
			margin-inline-end: variables.$db-spacing-fixed-2xs;
		}
	}

	&[data-hide-label="true"] {
		font-size: 0;

		input {
			margin-inline-end: 0;
		}
	}
}
