/**
 * Copyright © 2012-2024 by Data Virtuality GmbH
 */
package org.teiid.jdbc;

import java.io.ByteArrayInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.teiid.runtime.EmbeddedConfiguration;
import org.teiid.runtime.EmbeddedServer;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author Dmitry Pogorelov
 * @date 2024.03.15
 */
public class TestAuthorizationInAsterisk {

	@BeforeEach
	public void setup() {
		es = new EmbeddedServer();
	}

	@AfterEach public void teardown() {
		es.stop();
	}

	private EmbeddedServer es;

	@Test
	public void testPermissions() throws Exception {
		EmbeddedConfiguration ec = new EmbeddedConfiguration();
		es.start(ec);
		es.deployVDB(new ByteArrayInputStream(new String(
				"<vdb name=\"asterisk-1\" version=\"1\">\n" +
				"<model name=\"test_tables_pg\" type=\"virtual\">\n" +
				"<metadata type = \"DDL\"><![CDATA[create view test_c as select 100 as c, 222 as d, 333 as e;]]></metadata></model>\n" +
				"<model name=\"schema1\" type=\"virtual\">\n" +
				"<metadata type = \"DDL\"><![CDATA[create view test1 as select * from test_tables_pg.test_c;]]></metadata></model>\n" +
				"<model name=\"test_schema\" type=\"virtual\">\n" +
				"<metadata type = \"DDL\"><![CDATA[CREATE VIEW test_view1(col1 string, col2 integer) as select 's', 1; CREATE VIEW test_view AS SELECT * FROM test_view1; CREATE VIEW test_view3 AS SELECT 2;" +
						"create view test1_1 as select * from schema1.test1; create view test1_2 as WITH A AS (select * from test_tables_pg.test_c) SELECT * FROM A]]></metadata></model>\n" +
				"<data-role name=\"myrole\" any-authenticated=\"true\">" +
				"        <permission>\n" +
				"            <resource-name>test_schema.test_view1</resource-name>\n" +
				"            <allow-read>true</allow-read> \n" +
				"        </permission>\n" +
				"        <permission>\n" +
				"            <resource-name>test_schema.test_view3</resource-name>\n" +
				"            <allow-read>true</allow-read> \n" +
				"        </permission>\n" +
				"        <permission>\n" +
				"            <resource-name>test_schema.test_view1.col1</resource-name>\n" +
				"            <allow-read>false</allow-read>\n" +
				"        </permission>\n" +
				"        <permission>\n" +
				"            <resource-name>test_schema.test_view</resource-name>\n" +
				"            <allow-read>true</allow-read> \n" +
				"        </permission>\n" +
				"        <permission>\n" + // views, it must be enough to have only this one permission
				"            <resource-name>test_schema</resource-name>\n" +
				"            <allow-read>true</allow-read> \n" +
				"            <allow-update>true</allow-update> \n" +
				"            <allow-delete>true</allow-delete> \n" +
				"            <allow-execute>true</allow-execute> \n" +
				"            <allow-alter>true</allow-alter> \n" +
				"        </permission>\n" +
				"        <permission>\n" +
				"            <resource-name>test_tables_pg.test_c.d</resource-name>\n" +
				"            <allow-read>false</allow-read> \n" +
				"            <allow-update>false</allow-update> \n" +
				"            <allow-delete>false</allow-delete> \n" +
				"            <allow-execute>false</allow-execute> \n" +
				"            <allow-alter>false</allow-alter> \n" +
				"        </permission>\n" +
				"        <permission>\n" +
				"            <resource-name>test_schema.test1_1.c</resource-name>\n" +
				"            <allow-read>false</allow-read> \n" +
				"            <allow-update>false</allow-update> \n" +
				"            <allow-delete>false</allow-delete> \n" +
				"            <allow-execute>false</allow-execute> \n" +
				"            <allow-alter>false</allow-alter> \n" +
				"        </permission>\n" +
				"</data-role>\n" +
				"</vdb>"
		).getBytes()));
		try (Connection c = es.getDriver().connect("jdbc:datavirtuality:asterisk-1", null)) {
			try (Statement s = c.createStatement()) {
				// case #1
				try (ResultSet rs = s.executeQuery("SELECT * FROM test_schema.test_view1")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(1, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #3
				try (ResultSet rs = s.executeQuery("WITH A AS (SELECT * FROM test_schema.test_view1) SELECT * FROM A")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(1, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #4
				try (ResultSet rs = s.executeQuery("select * from test_schema.test_view")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(1, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #5
				try (ResultSet rs = s.executeQuery("SELECT * FROM (SELECT * FROM test_schema.test_view1) x")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(1, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #6
				try (ResultSet rs = s.executeQuery("SELECT * FROM test_schema.test_view3 UNION SELECT * FROM test_schema.test_view1")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(2, rs.getInt(1));
					rs.next();
					assertEquals(1, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #7
				try (ResultSet rs = s.executeQuery("SELECT * FROM test_schema.test1_1")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(1, rsmd.getColumnCount());
					rs.next();
					assertEquals(333, rs.getInt(1));
					assertEquals(false, rs.next());
				}
				// case #8
				try (ResultSet rs = s.executeQuery("SELECT * FROM test_schema.test1_2")) {
					ResultSetMetaData rsmd = rs.getMetaData();
					assertEquals(2, rsmd.getColumnCount());
					rs.next();
					assertEquals(100, rs.getInt(1));
					assertEquals(333, rs.getInt(2));
					assertEquals(false, rs.next());
				}
			}
		}
	}
}
