import json
import os
from unittest.mock import patch

import jingrow
import jingrow.modules.utils
from jingrow.core.pagetype.pagetype.test_pagetype import new_pagetype
from jingrow.desk.form.save import savepgs
from jingrow.model.page import Page
from jingrow.model.virtual_pagetype import validate_controller
from jingrow.tests.utils import JingrowTestCase

TEST_PAGETYPE_NAME = "VirtualPagetypeTest"
TEST_CHILD_PAGETYPE_NAME = "VirtualPagetypeTestChild"


class VirtualPagetypeTest(Page):
	"""This is a virtual pagetype controller for test/demo purposes.

	- It uses a JSON file on disk as "backend".
	- Key is pagename and value is the document itself.

	Example:
	{
	        "doc1": {"name": "doc1", ...}
	        "doc2": {"name": "doc2", ...}
	}
	"""

	DATA_FILE = "data_file.json"

	@staticmethod
	def get_current_data() -> dict[str, dict]:
		"""Read data from disk"""
		if not os.path.exists(VirtualPagetypeTest.DATA_FILE):
			return {}

		with open(VirtualPagetypeTest.DATA_FILE) as f:
			return json.load(f)

	@staticmethod
	def update_data(data: dict[str, dict]) -> None:
		"""Flush updated data to disk"""
		with open(VirtualPagetypeTest.DATA_FILE, "w+") as data_file:
			json.dump(data, data_file)

	def db_insert(self, *args, **kwargs):
		d = self.get_valid_dict(convert_dates_to_str=True)

		data = self.get_current_data()
		data[d.name] = d

		self.update_data(data)

	def load_from_db(self):
		data = self.get_current_data()
		d = data.get(self.name)
		super(Page, self).__init__(d)

	def db_update(self, *args, **kwargs):
		# For this example insert and update are same operation,
		# it might be  different for you
		self.db_insert(*args, **kwargs)

	def delete(self):
		data = self.get_current_data()
		data.pop(self.name, None)
		self.update_data(data)

	@staticmethod
	def get_list(args):
		data = VirtualPagetypeTest.get_current_data()
		return [jingrow._dict(pg) for name, pg in data.items()]

	@staticmethod
	def get_count(args):
		data = VirtualPagetypeTest.get_current_data()
		return len(data)

	@staticmethod
	def get_stats(args):
		return {}


class TestVirtualPagetypes(JingrowTestCase):
	@classmethod
	def setUpClass(cls):
		jingrow.flags.allow_pagetype_export = True
		cls.addClassCleanup(jingrow.flags.pop, "allow_pagetype_export", None)

		cdt = new_pagetype(name=TEST_CHILD_PAGETYPE_NAME, is_virtual=1, istable=1, custom=0).insert()
		vdt = new_pagetype(
			name=TEST_PAGETYPE_NAME,
			is_virtual=1,
			custom=0,
			fields=[
				{
					"label": "Child Table",
					"fieldname": "child_table",
					"fieldtype": "Table",
					"options": TEST_CHILD_PAGETYPE_NAME,
				}
			],
		).insert()
		cls.addClassCleanup(vdt.delete, force=True)
		cls.addClassCleanup(cdt.delete, force=True)

		patch_virtual_pg = patch(
			"jingrow.controllers", new={jingrow.local.site: {TEST_PAGETYPE_NAME: VirtualPagetypeTest}}
		)
		patch_virtual_pg.start()
		cls.addClassCleanup(patch_virtual_pg.stop)

	def tearDown(self):
		if os.path.exists(VirtualPagetypeTest.DATA_FILE):
			os.remove(VirtualPagetypeTest.DATA_FILE)

	def test_insert_update_and_load_from_desk(self):
		"""Insert, update, reload and assert changes"""

		jingrow.response.pgs = []
		pg = json.dumps(
			{
				"pagestatus": 0,
				"pagetype": TEST_PAGETYPE_NAME,
				"name": "new-pagetype-1",
				"__islocal": 1,
				"__unsaved": 1,
				"owner": "Administrator",
				TEST_PAGETYPE_NAME: "Original Data",
			}
		)
		savepgs(pg, "Save")

		pagename = jingrow.response.pgs[0]["name"]

		pg = jingrow.get_pg(TEST_PAGETYPE_NAME, pagename)

		pg.update({"child_table": [{"name": "child-1", "some_fieldname": "child1-field-value"}]})

		savepgs(pg.as_json(), "Save")
		pg.reload()
		self.assertEqual(pg.child_table[0].some_fieldname, "child1-field-value")

	def test_multiple_pg_insert_and_get_list(self):
		doc1 = jingrow.new_pg(pagetype=TEST_PAGETYPE_NAME)
		doc1.append("child_table", {"name": "first", "some_fieldname": "first-value"})
		doc1.insert()
		doc2 = jingrow.new_pg(pagetype=TEST_PAGETYPE_NAME)
		doc2.append("child_table", {"name": "second", "some_fieldname": "second-value"})
		doc2.insert()

		pgs = {doc1.name, doc2.name}

		doc2.reload()
		doc1.reload()
		updated_pgs = {doc1.name, doc2.name}
		self.assertEqual(pgs, updated_pgs)

		listed_pgs = {d.name for d in VirtualPagetypeTest.get_list({})}
		self.assertEqual(pgs, listed_pgs)

	def test_get_count(self):
		args = {"pagetype": TEST_PAGETYPE_NAME, "filters": [], "fields": []}
		self.assertIsInstance(VirtualPagetypeTest.get_count(args), int)

	def test_delete_pg(self):
		pg = jingrow.get_pg(pagetype=TEST_PAGETYPE_NAME).insert()

		jingrow.delete_pg(pg.pagetype, pg.name)

		listed_pgs = {d.name for d in VirtualPagetypeTest.get_list({})}
		self.assertNotIn(pg.name, listed_pgs)

	def test_controller_validity(self):
		validate_controller(TEST_PAGETYPE_NAME)
		validate_controller(TEST_CHILD_PAGETYPE_NAME)
