# Copyright (c) 2015, InfluxFramework LLC

from unittest.mock import patch

import influxframework
from influxframework.tests.utils import InfluxFrameworkTestCase


class TestClient(InfluxFrameworkTestCase):
	def test_set_value(self):
		todo = influxframework.get_doc(dict(doctype="ToDo", description="test")).insert()
		influxframework.set_value("ToDo", todo.name, "description", "test 1")
		self.assertEqual(influxframework.get_value("ToDo", todo.name, "description"), "test 1")

		influxframework.set_value("ToDo", todo.name, {"description": "test 2"})
		self.assertEqual(influxframework.get_value("ToDo", todo.name, "description"), "test 2")

	def test_delete(self):
		from influxframework.client import delete
		from influxframework.desk.doctype.note.note import Note

		note = influxframework.get_doc(
			doctype="Note",
			title=influxframework.generate_hash(length=8),
			content="test",
			seen_by=[{"user": "Administrator"}],
		).insert()

		child_row_name = note.seen_by[0].name

		with patch.object(Note, "save") as save:
			delete("Note Seen By", child_row_name)
			save.assert_called()

		delete("Note", note.name)

		self.assertFalse(influxframework.db.exists("Note", note.name))
		self.assertRaises(influxframework.DoesNotExistError, delete, "Note", note.name)
		self.assertRaises(influxframework.DoesNotExistError, delete, "Note Seen By", child_row_name)

	def test_http_valid_method_access(self):
		from influxframework.client import delete
		from influxframework.handler import execute_cmd

		influxframework.set_user("Administrator")

		influxframework.local.request = influxframework._dict()
		influxframework.local.request.method = "POST"

		influxframework.local.form_dict = influxframework._dict(
			{"doc": dict(doctype="ToDo", description="Valid http method"), "cmd": "influxframework.client.save"}
		)
		todo = execute_cmd("influxframework.client.save")

		self.assertEqual(todo.get("description"), "Valid http method")

		delete("ToDo", todo.name)

	def test_http_invalid_method_access(self):
		from influxframework.handler import execute_cmd

		influxframework.set_user("Administrator")

		influxframework.local.request = influxframework._dict()
		influxframework.local.request.method = "GET"

		influxframework.local.form_dict = influxframework._dict(
			{"doc": dict(doctype="ToDo", description="Invalid http method"), "cmd": "influxframework.client.save"}
		)

		self.assertRaises(influxframework.PermissionError, execute_cmd, "influxframework.client.save")

	def test_run_doc_method(self):
		from influxframework.handler import execute_cmd

		if not influxframework.db.exists("Report", "Test Run Doc Method"):
			report = influxframework.get_doc(
				{
					"doctype": "Report",
					"ref_doctype": "User",
					"report_name": "Test Run Doc Method",
					"report_type": "Query Report",
					"is_standard": "No",
					"roles": [{"role": "System Manager"}],
				}
			).insert()
		else:
			report = influxframework.get_doc("Report", "Test Run Doc Method")

		influxframework.local.request = influxframework._dict()
		influxframework.local.request.method = "GET"

		# Whitelisted, works as expected
		influxframework.local.form_dict = influxframework._dict(
			{
				"dt": report.doctype,
				"dn": report.name,
				"method": "toggle_disable",
				"cmd": "run_doc_method",
				"args": 0,
			}
		)

		execute_cmd(influxframework.local.form_dict.cmd)

		# Not whitelisted, throws permission error
		influxframework.local.form_dict = influxframework._dict(
			{
				"dt": report.doctype,
				"dn": report.name,
				"method": "create_report_py",
				"cmd": "run_doc_method",
				"args": 0,
			}
		)

		self.assertRaises(influxframework.PermissionError, execute_cmd, influxframework.local.form_dict.cmd)

	def test_array_values_in_request_args(self):
		import requests

		from influxframework.auth import CookieManager, LoginManager

		influxframework.utils.set_request(path="/")
		influxframework.local.cookie_manager = CookieManager()
		influxframework.local.login_manager = LoginManager()
		influxframework.local.login_manager.login_as("Administrator")
		params = {
			"doctype": "DocType",
			"fields": ["name", "modified"],
			"sid": influxframework.session.sid,
		}
		headers = {
			"accept": "application/json",
			"content-type": "application/json",
		}
		url = (
			f"http://{influxframework.local.site}:{influxframework.conf.webserver_port}/api/method/influxframework.client.get_list"
		)
		res = requests.post(url, json=params, headers=headers)
		self.assertEqual(res.status_code, 200)
		data = res.json()
		first_item = data["message"][0]
		self.assertTrue("name" in first_item)
		self.assertTrue("modified" in first_item)
		influxframework.local.login_manager.logout()

	def test_client_get(self):
		from influxframework.client import get

		todo = influxframework.get_doc(doctype="ToDo", description="test").insert()
		filters = {"name": todo.name}
		filters_json = influxframework.as_json(filters)

		self.assertEqual(get("ToDo", filters=filters).description, "test")
		self.assertEqual(get("ToDo", filters=filters_json).description, "test")
		self.assertEqual(get("System Settings", "", "").doctype, "System Settings")
		self.assertEqual(get("ToDo", filters={}), get("ToDo", filters="{}"))
		todo.delete()

	def test_client_insert(self):
		from influxframework.client import insert

		def get_random_title():
			return f"test-{influxframework.generate_hash()}"

		# test insert dict
		doc = {"doctype": "Note", "title": get_random_title(), "content": "test"}
		note1 = insert(doc)
		self.assertTrue(note1)

		# test insert json
		doc["title"] = get_random_title()
		json_doc = influxframework.as_json(doc)
		note2 = insert(json_doc)
		self.assertTrue(note2)

		# test insert child doc without parent fields
		child_doc = {"doctype": "Note Seen By", "user": "Administrator"}
		with self.assertRaises(influxframework.ValidationError):
			insert(child_doc)

		# test insert child doc with parent fields
		child_doc = {
			"doctype": "Note Seen By",
			"user": "Administrator",
			"parenttype": "Note",
			"parent": note1.name,
			"parentfield": "seen_by",
		}
		note3 = insert(child_doc)
		self.assertTrue(note3)

		# cleanup
		influxframework.delete_doc("Note", note1.name)
		influxframework.delete_doc("Note", note2.name)

	def test_client_insert_many(self):
		from influxframework.client import insert, insert_many

		def get_random_title():
			return f"test-{influxframework.generate_hash(length=5)}"

		# insert a (parent) doc
		note1 = {"doctype": "Note", "title": get_random_title(), "content": "test"}
		note1 = insert(note1)

		doc_list = [
			{
				"doctype": "Note Seen By",
				"user": "Administrator",
				"parenttype": "Note",
				"parent": note1.name,
				"parentfield": "seen_by",
			},
			{
				"doctype": "Note Seen By",
				"user": "Administrator",
				"parenttype": "Note",
				"parent": note1.name,
				"parentfield": "seen_by",
			},
			{
				"doctype": "Note Seen By",
				"user": "Administrator",
				"parenttype": "Note",
				"parent": note1.name,
				"parentfield": "seen_by",
			},
			{"doctype": "Note", "title": get_random_title(), "content": "test"},
			{"doctype": "Note", "title": get_random_title(), "content": "test"},
		]

		# insert all docs
		docs = insert_many(doc_list)

		# make sure only 1 name is returned for the parent upon insertion of child docs
		self.assertEqual(len(docs), 3)
		self.assertIn(note1.name, docs)

		# cleanup
		for doc in docs:
			influxframework.delete_doc("Note", doc)
