package marketing.heyday.hartmann.fotodocumentation.rest;

import java.io.*;
import java.nio.charset.Charset;
import java.sql.*;
import java.util.Date;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONAssert;

/**
 * 
 * <p>Copyright: Copyright (c) 2024</p>
 * <p>Company: heyday Marketing GmbH</p>
 * @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
 * @version 1.0
 * 
 * created: 13 Nov 2024
 */

public abstract class AbstractTest {
	private static final Log LOG = LogFactory.getLog(AbstractTest.class);

	public String deploymentURL = "http://localhost:8180/";
	public String username = "admin";
	public String password = "test";

	private static Properties props = null;

	public static Connection getConnection() throws SQLException {
		if (props == null) {
			try (FileReader reader = new FileReader(new File("src/test/resources/junit.properties"))) {
				Class.forName("org.postgresql.Driver");
				props = new Properties();
				props.load(reader);
			} catch (Exception e) {
				throw new SQLException(e.getMessage());
			}
		}
		return DriverManager.getConnection(props.getProperty("connection"), props.getProperty("username"), props.getProperty("password"));
	}

	public static void initDB() {
		initDB("dataset.xml");
	}

	public static void initDB(String xmlFile) {
		try (Connection jdbcConnection = getConnection();) {
			initDb(jdbcConnection, xmlFile);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	private static void initDb(Connection jdbcConnection, String xmlFile) {
		// using manual DBUnit since the persistence plugin has performance problems.
		// https://community.jboss.org/thread/235511
		// https://issues.jboss.org/browse/ARQ-1440
		try {
			System.out.println("start dbunit " + new Date(System.currentTimeMillis()));
			IDatabaseConnection conn = new org.dbunit.database.DatabaseConnection(jdbcConnection);
			DatabaseConfig config = conn.getConfig();
			config.setProperty("http://www.dbunit.org/features/datatypeWarning", Boolean.FALSE);
			config.setProperty(DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS, Boolean.TRUE);
			config.setProperty(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, Boolean.TRUE);
			config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory());

			// initialize your dataset here
			IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File("src/test/resources/datasets/" + xmlFile));
			ReplacementDataSet repDataSet = new ReplacementDataSet(dataSet);
			repDataSet.addReplacementObject("EXP_DATE", DateUtils.addDays(new Date(), 1));
			repDataSet.addReplacementObject("NULL_DATE", null);
			repDataSet.addReplacementObject("NULL_NUMBER", null);
			repDataSet.addReplacementObject("NULL_STRING", null);
			try {
				DatabaseOperation.CLEAN_INSERT.execute(conn, repDataSet);
			} finally {
				conn.close();
			}
		} catch (java.sql.BatchUpdateException e) {
			e.printStackTrace();
			e.getNextException().printStackTrace();
			throw new RuntimeException(e.getMessage(), e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage(), e);
		}
		System.out.println("End dbunit " + new Date(System.currentTimeMillis()));
	}

	public String fileToString(final String fileName) {
		try (FileInputStream input = new FileInputStream("src/test/resources/" + fileName)) {
			return IOUtils.toString(input, Charset.forName("utf-8"));
		} catch (IOException e) {
			return "";
		}
	}

	public static void writeJsonFile(final String content, final String fileName) {
		if (content.isEmpty()) {
			return;
		}
		File file = new File("target/test/output/");
		file.mkdirs();
		try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
			ObjectMapper mapper = new ObjectMapper();
			Object json = mapper.readValue(content, Object.class);
			String formattedContent = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
			System.out.println(formattedContent);
			IOUtils.write(formattedContent, out, Charset.forName("utf-8"));
		} catch (Exception e) {
			LOG.error("Error storing json file", e);
		}
	}

	public static void writeFile(byte[] content, String fileName) {
		File file = new File("target/test/output/");
		file.mkdirs();

		try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
			IOUtils.write(content, out);
		} catch (Exception e) {
			LOG.error("Error storing binary file", e);
		}
	}

	public static byte[] readFile(String fileName) {
		try (FileInputStream in = new FileInputStream("src/test/resources/" + fileName)) {
			return IOUtils.toByteArray(in);
		} catch (Exception e) {
			throw new RuntimeException("Error reading file src/test/resources/" + fileName);
		}
	}

	public void writeFile(InputStream content, String fileName) {
		File file = new File("target/test/output/");
		file.mkdirs();

		try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
			IOUtils.copy(content, out);
		} catch (Exception e) {
			LOG.error("Error storing binary file", e);
		}
	}

	public static int getCount(String sql) {
		try (Connection conn = getConnection(); PreparedStatement statement = conn.prepareStatement(sql); ResultSet resultSet = statement.executeQuery();) {
			if (resultSet.next()) {
				return resultSet.getInt(1);
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
		LOG.error("no result found");
		return 0;
	}

	protected void jsonAssert(String expected, String content) {
		jsonAssert(expected, content, false);
	}

	protected void jsonAssert(String expected, String content, boolean strict) {
		try {
			JSONAssert.assertEquals(expected, content, strict);
		} catch (JSONException e) {
			throw new RuntimeException(e);
		}
	}
}
