package org.tinylog.impl.format.style;

import java.sql.Types;

import org.junit.jupiter.api.Test;
import org.tinylog.impl.LogEntry;
import org.tinylog.impl.format.SqlRecord;
import org.tinylog.impl.format.placeholder.BundlePlaceholder;
import org.tinylog.impl.format.placeholder.FilePlaceholder;
import org.tinylog.impl.format.placeholder.LinePlaceholder;
import org.tinylog.impl.format.placeholder.MessageOnlyPlaceholder;
import org.tinylog.impl.format.placeholder.MessagePlaceholder;
import org.tinylog.impl.format.placeholder.Placeholder;
import org.tinylog.impl.format.placeholder.StaticTextPlaceholder;
import org.tinylog.impl.test.LogEntryBuilder;
import org.tinylog.impl.test.PlaceholderRenderer;

import com.google.common.collect.ImmutableList;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;

class AbstractStylePlaceholderTest {

	/**
	 * Verifies that a style placeholder requires the same log entries as the actual wrapped placeholder.
	 */
	@Test
	void requiredLogEntryValues() {
		Placeholder actual = new MessagePlaceholder();
		Placeholder styled = new StylePlaceholder(actual);
		assertThat(styled.getRequiredLogEntryValues()).isEqualTo(actual.getRequiredLogEntryValues());
	}

	/**
	 * Verifies that a {@link Types#VARCHAR} placeholder is rendered correctly.
	 */
	@Test
	void renderVarCharPlaceholder() {
		Placeholder styled = new StylePlaceholder(new FilePlaceholder());
		PlaceholderRenderer renderer = new PlaceholderRenderer(styled);

		LogEntry logEntry = new LogEntryBuilder().fileName("Foo.java").create();
		assertThat(renderer.render(logEntry)).isEqualTo("[Foo.java]");

		logEntry = new LogEntryBuilder().create();
		assertThat(renderer.render(logEntry)).isEqualTo("[<file unknown>]");
	}

	/**
	 * Verifies that a {@link Types#LONGVARCHAR} placeholder is rendered correctly.
	 */
	@Test
	void renderLongVarCharPlaceholder() {
		Placeholder styled = new StylePlaceholder(new MessageOnlyPlaceholder());
		PlaceholderRenderer renderer = new PlaceholderRenderer(styled);

		LogEntry logEntry = new LogEntryBuilder().message("Hello World!").create();
		assertThat(renderer.render(logEntry)).isEqualTo("[Hello World!]");

		logEntry = new LogEntryBuilder().create();
		assertThat(renderer.render(logEntry)).isEqualTo("[]");
	}

	/**
	 * Verifies that a non-string placeholder is rendered correctly.
	 */
	@Test
	void renderNumericPlaceholder() {
		Placeholder styled = new StylePlaceholder(new LinePlaceholder());
		PlaceholderRenderer renderer = new PlaceholderRenderer(styled);

		LogEntry logEntry = new LogEntryBuilder().lineNumber(42).create();
		assertThat(renderer.render(logEntry)).isEqualTo("[42]");

		logEntry = new LogEntryBuilder().create();
		assertThat(renderer.render(logEntry)).isEqualTo("[?]");
	}

	/**
	 * Verifies that a bundled placeholder is rendered correctly.
	 */
	@Test
	void renderBundledPlaceholder() {
		Placeholder prefix = new StaticTextPlaceholder("foo:");
		Placeholder styled = spy(new StylePlaceholder(new MessageOnlyPlaceholder()));
		PlaceholderRenderer renderer = new PlaceholderRenderer(new BundlePlaceholder(ImmutableList.of(prefix, styled)));

		LogEntry logEntry = new LogEntryBuilder().message("Hello World!").create();
		assertThat(renderer.render(logEntry)).isEqualTo("foo:[Hello World!]");

		logEntry = new LogEntryBuilder().create();
		assertThat(renderer.render(logEntry)).isEqualTo("foo:[]");
	}

	/**
	 * Verifies that a {@link Types#VARCHAR} placeholder is resolved correctly.
	 */
	@Test
	void resolveVarCharPlaceholder() {
		Placeholder styled = new StylePlaceholder(new FilePlaceholder());

		LogEntry logEntry = new LogEntryBuilder().fileName("Foo.java").create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.VARCHAR, "[Foo.java]"));

		logEntry = new LogEntryBuilder().create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.VARCHAR, null));
	}

	/**
	 * Verifies that a {@link Types#LONGVARCHAR} placeholder is resolved correctly.
	 */
	@Test
	void resolveLongCharPlaceholder() {
		Placeholder styled = new StylePlaceholder(new MessageOnlyPlaceholder());

		LogEntry logEntry = new LogEntryBuilder().message("Hello World!").create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.LONGVARCHAR, "[Hello World!]"));

		logEntry = new LogEntryBuilder().create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.LONGVARCHAR, null));
	}

	/**
	 * Verifies that a non-string placeholder is resolved as {@link Types#VARCHAR}.
	 */
	@Test
	void resolveNumericPlaceholder() {
		Placeholder styled = new StylePlaceholder(new LinePlaceholder());

		LogEntry logEntry = new LogEntryBuilder().lineNumber(42).create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.VARCHAR, "[42]"));

		logEntry = new LogEntryBuilder().create();
		assertThat(styled.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.VARCHAR, null));
	}

	/**
	 * Verifies that a bundled placeholder is resolved as {@link Types#LONGVARCHAR}.
	 */
	@Test
	void resolveBundledPlaceholder() {
		Placeholder prefix = new StaticTextPlaceholder("foo:");
		Placeholder styled = spy(new StylePlaceholder(new MessageOnlyPlaceholder()));
		Placeholder bundle = new BundlePlaceholder(ImmutableList.of(prefix, styled));

		LogEntry logEntry = new LogEntryBuilder().message("Hello World!").create();
		assertThat(bundle.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.LONGVARCHAR, "foo:[Hello World!]"));

		logEntry = new LogEntryBuilder().create();
		assertThat(bundle.resolve(logEntry))
			.usingRecursiveComparison()
			.isEqualTo(new SqlRecord<>(Types.LONGVARCHAR, "foo:"));
	}

	/**
	 * Testable non-abstract implementation of {@link AbstractStylePlaceholder}.
	 */
	private static final class StylePlaceholder extends AbstractStylePlaceholder {

		/**
		 * @param placeholder The actual placeholder to style
		 */
		private StylePlaceholder(Placeholder placeholder) {
			super(placeholder);
		}

		@Override
		protected void apply(StringBuilder builder, int start) {
			builder.insert(start, '[');
			builder.append(']');
		}

	}

}

@@ -809,47 +809,8 @@
 hat;
-%0Aimport static org.mockito.Mockito.spy;
 %0A%0Acl
@@ -2935,36 +2935,32 @@
 holder styled = 
-spy(
 new StylePlaceho
@@ -2985,33 +2985,32 @@
 lyPlaceholder())
-)
 ;%0A%09%09PlaceholderR
@@ -5346,12 +5346,8 @@
 d = 
-spy(
 new 
@@ -5392,17 +5392,16 @@
 older())
-)
 ;%0A%09%09Plac

Remove unneeded Mockito spy