# Copyright (c) 2019, JINGROW and Contributors
# License: MIT. See LICENSE

import time

import sqlparse

import jingrow
import jingrow.recorder
from jingrow.recorder import normalize_query
from jingrow.tests.utils import JingrowTestCase, timeout
from jingrow.utils import set_request
from jingrow.utils.doctor import any_job_pending
from jingrow.website.serve import get_response_content


class TestRecorder(JingrowTestCase):
	def setUp(self):
		self.wait_for_background_jobs()
		jingrow.recorder.stop()
		jingrow.recorder.delete()
		set_request()
		jingrow.recorder.start()
		jingrow.recorder.record()

	@timeout
	def wait_for_background_jobs(self):
		while any_job_pending(jingrow.local.site):
			time.sleep(1)

	def stop_recording(self):
		jingrow.recorder.dump()
		jingrow.recorder.stop()

	def test_start(self):
		self.stop_recording()
		requests = jingrow.recorder.get()
		self.assertEqual(len(requests), 1)

	def test_do_not_record(self):
		jingrow.recorder.do_not_record(jingrow.get_all)("PageType")
		self.stop_recording()
		requests = jingrow.recorder.get()
		self.assertEqual(len(requests), 0)

	def test_get(self):
		self.stop_recording()

		requests = jingrow.recorder.get()
		self.assertEqual(len(requests), 1)

		request = jingrow.recorder.get(requests[0]["uuid"])
		self.assertTrue(request)

	def test_delete(self):
		self.stop_recording()

		requests = jingrow.recorder.get()
		self.assertEqual(len(requests), 1)

		jingrow.recorder.delete()

		requests = jingrow.recorder.get()
		self.assertEqual(len(requests), 0)

	def test_record_without_sql_queries(self):
		self.stop_recording()

		requests = jingrow.recorder.get()
		request = jingrow.recorder.get(requests[0]["uuid"])

		self.assertEqual(len(request["calls"]), 0)

	def test_record_with_sql_queries(self):
		jingrow.get_all("PageType")
		self.stop_recording()

		requests = jingrow.recorder.get()
		request = jingrow.recorder.get(requests[0]["uuid"])

		self.assertNotEqual(len(request["calls"]), 0)

	def test_explain(self):
		jingrow.db.sql("SELECT * FROM tabPageType")
		jingrow.db.sql("COMMIT")
		jingrow.db.sql("select 1", run=0)
		self.stop_recording()

		requests = jingrow.recorder.get()
		request = jingrow.recorder.get(requests[0]["uuid"])

		self.assertEqual(len(request["calls"][0]["explain_result"]), 1)
		self.assertEqual(len(request["calls"][1]["explain_result"]), 0)

	def test_multiple_queries(self):
		queries = [
			{"mariadb": "SELECT * FROM tabPageType", "postgres": 'SELECT * FROM "tabPageType"'},
			{"mariadb": "SELECT COUNT(*) FROM tabPageType", "postgres": 'SELECT COUNT(*) FROM "tabPageType"'},
			{"mariadb": "COMMIT", "postgres": "COMMIT"},
		]

		sql_dialect = jingrow.db.db_type or "mariadb"
		for query in queries:
			jingrow.db.sql(query[sql_dialect])

		self.stop_recording()

		requests = jingrow.recorder.get()
		request = jingrow.recorder.get(requests[0]["uuid"])

		self.assertEqual(len(request["calls"]), len(queries))

		for query, call in zip(queries, request["calls"], strict=False):
			self.assertEqual(
				call["query"],
				sqlparse.format(
					query[sql_dialect].strip(), keyword_case="upper", reindent=True, strip_comments=True
				),
			)

	def test_duplicate_queries(self):
		queries = [
			("SELECT * FROM tabPageType", 2),
			("SELECT COUNT(*) FROM tabPageType", 1),
			("select * from tabPageType", 2),
			("COMMIT", 3),
			("COMMIT", 3),
			("COMMIT", 3),
		]
		for query in queries:
			jingrow.db.sql(query[0])

		self.stop_recording()

		requests = jingrow.recorder.get()
		request = jingrow.recorder.get(requests[0]["uuid"])

		for query, call in zip(queries, request["calls"], strict=False):
			self.assertEqual(call["exact_copies"], query[1])

	def test_error_page_rendering(self):
		content = get_response_content("error")
		self.assertIn("Error", content)


class TestRecorderDeco(JingrowTestCase):
	def test_recorder_flag(self):
		jingrow.recorder.delete()

		@jingrow.recorder.record_queries
		def test():
			jingrow.get_all("User")

		test()
		self.assertTrue(jingrow.recorder.get())


class TestQueryNormalization(JingrowTestCase):
	def test_query_normalization(self):
		test_cases = {
			"select * from user where name = 'x'": "select * from user where name = ?",
			"select * from user where a > 5": "select * from user where a > ?",
			"select * from `user` where a > 5": "select * from `user` where a > ?",
			"select `name` from `user`": "select `name` from `user`",
			"select `name` from `user` limit 10": "select `name` from `user` limit ?",
			"select `name` from `user` where name in ('a', 'b', 'c')": "select `name` from `user` where name in (?)",
		}

		for query, normalized in test_cases.items():
			self.assertEqual(normalize_query(query), normalized)
