# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt


import json
import os

import frappe
from frappe import _


def setup_taxes_and_charges(company_name: str, country: str):
	if not frappe.db.exists("Company", company_name):
		frappe.throw(_("Company {} does not exist yet. Taxes setup aborted.").format(company_name))

	file_path = os.path.join(os.path.dirname(__file__), "..", "data", "country_wise_tax.json")
	with open(file_path) as json_file:
		tax_data = json.load(json_file)

	country_wise_tax = tax_data.get(country)

	if not country_wise_tax:
		return

	if "chart_of_accounts" not in country_wise_tax:
		country_wise_tax = simple_to_detailed(country_wise_tax)

	from_detailed_data(company_name, country_wise_tax)
	update_regional_tax_settings(country, company_name)


def simple_to_detailed(templates):
	"""
	Convert a simple taxes object into a more detailed data structure.

	Example input:

	{
	        "France VAT 20%": {
	                "account_name": "VAT 20%",
	                "tax_rate": 20,
	                "default": 1
	        },
	        "France VAT 10%": {
	                "account_name": "VAT 10%",
	                "tax_rate": 10
	        }
	}
	"""
	return {
		"chart_of_accounts": {
			"*": {
				"item_tax_templates": [
					{
						"title": title,
						"taxes": [
							{
								"tax_type": {
									"account_name": data.get("account_name"),
									"tax_rate": data.get("tax_rate"),
								}
							}
						],
					}
					for title, data in templates.items()
				],
				"*": [
					{
						"title": title,
						"is_default": data.get("default", 0),
						"taxes": [
							{
								"account_head": {
									"account_name": data.get("account_name"),
									"tax_rate": data.get("tax_rate"),
								}
							}
						],
					}
					for title, data in templates.items()
				],
			}
		}
	}


def from_detailed_data(company_name, data):
	"""Create Taxes and Charges Templates from detailed data."""
	charts_company_name = company_name
	if (
		frappe.db.get_value("Company", company_name, "create_chart_of_accounts_based_on")
		== "Existing Company"
	):
		charts_company_name = frappe.db.get_value("Company", company_name, "existing_company")
	coa_name = frappe.db.get_value("Company", charts_company_name, "chart_of_accounts")
	coa_data = data.get("chart_of_accounts", {})
	tax_templates = coa_data.get(coa_name) or coa_data.get("*", {})
	tax_categories = data.get("tax_categories")
	sales_tax_templates = tax_templates.get("sales_tax_templates") or tax_templates.get("*", {})
	purchase_tax_templates = tax_templates.get("purchase_tax_templates") or tax_templates.get("*", {})
	item_tax_templates = tax_templates.get("item_tax_templates") or tax_templates.get("*", {})

	if tax_categories:
		for tax_category in tax_categories:
			make_tax_category(tax_category)

	if sales_tax_templates:
		for template in sales_tax_templates:
			make_taxes_and_charges_template(company_name, "Sales Taxes and Charges Template", template)

	if purchase_tax_templates:
		for template in purchase_tax_templates:
			make_taxes_and_charges_template(company_name, "Purchase Taxes and Charges Template", template)

	if item_tax_templates:
		for template in item_tax_templates:
			make_item_tax_template(company_name, template)


def update_regional_tax_settings(country, company):
	path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
	if os.path.exists(path.encode("utf-8")):
		try:
			module_name = f"erpnext.regional.{frappe.scrub(country)}.setup.update_regional_tax_settings"
			frappe.get_attr(module_name)(country, company)
		except (ImportError, AttributeError):
			pass
		except Exception:
			# Log error and ignore if failed to setup regional tax settings
			frappe.log_error("Unable to setup regional tax settings")


def make_taxes_and_charges_template(company_name, doctype, template):
	template["company"] = company_name
	template["doctype"] = doctype

	if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
		return

	for tax_row in template.get("taxes"):
		account_data = tax_row.get("account_head")
		tax_row_defaults = {
			"category": "Total",
			"charge_type": "On Net Total",
			"cost_center": frappe.db.get_value("Company", company_name, "cost_center"),
		}

		if doctype == "Purchase Taxes and Charges Template":
			tax_row_defaults["add_deduct_tax"] = "Add"

		# if account_head is a dict, search or create the account and get it's name
		if isinstance(account_data, dict):
			tax_row_defaults["description"] = "{} @ {}".format(
				account_data.get("account_name"), account_data.get("tax_rate")
			)
			tax_row_defaults["rate"] = account_data.get("tax_rate")
			account = get_or_create_account(company_name, account_data)
			tax_row["account_head"] = account.name

		# use the default value if nothing other is specified
		for fieldname, default_value in tax_row_defaults.items():
			if fieldname not in tax_row:
				tax_row[fieldname] = default_value

	doc = frappe.get_doc(template)

	# Data in country wise json is already pre validated, hence validations can be ignored
	# Ingone validations to make doctypes faster
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.flags.ignore_mandatory = True
	doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
	return doc


def make_item_tax_template(company_name, template):
	"""Create an Item Tax Template.

	This requires a separate method because Item Tax Template is structured
	differently from Sales and Purchase Tax Templates.
	"""
	doctype = "Item Tax Template"
	template["company"] = company_name
	template["doctype"] = doctype

	if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
		return

	for tax_row in template.get("taxes"):
		account_data = tax_row.get("tax_type")

		# if tax_type is a dict, search or create the account and get it's name
		if isinstance(account_data, dict):
			account = get_or_create_account(company_name, account_data)
			tax_row["tax_type"] = account.name
			if "tax_rate" not in tax_row:
				tax_row["tax_rate"] = account_data.get("tax_rate")

	doc = frappe.get_doc(template)

	# Data in country wise json is already pre validated, hence validations can be ignored
	# Ingone validations to make doctypes faster
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
	return doc


def get_or_create_account(company_name, account):
	"""
	Check if account already exists. If not, create it.
	Return a tax account or None.
	"""
	default_root_type = "Liability"
	root_type = account.get("root_type", default_root_type)

	or_filters = {"account_name": account.get("account_name")}
	if account.get("account_number"):
		or_filters.update({"account_number": account.get("account_number")})

	existing_accounts = frappe.get_all(
		"Account", filters={"company": company_name, "root_type": root_type}, or_filters=or_filters
	)

	if existing_accounts:
		return frappe.get_doc("Account", existing_accounts[0].name)

	tax_group = get_or_create_tax_group(company_name, root_type)

	account["doctype"] = "Account"
	account["company"] = company_name
	account["parent_account"] = tax_group
	account["report_type"] = "Balance Sheet"
	account["account_type"] = "Tax"
	account["root_type"] = root_type
	account["is_group"] = 0

	doc = frappe.get_doc(account)
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.insert(ignore_permissions=True, ignore_mandatory=True, ignore_if_duplicate=True)
	return doc


def get_or_create_tax_group(company_name, root_type):
	# Look for a group account of type 'Tax'
	tax_group_name = frappe.db.get_value(
		"Account",
		{"is_group": 1, "root_type": root_type, "account_type": "Tax", "company": company_name},
	)

	if tax_group_name:
		return tax_group_name

	# Look for a group account named 'Duties and Taxes' or 'Tax Assets'
	account_name = _("Duties and Taxes") if root_type == "Liability" else _("Tax Assets")
	tax_group_name = frappe.db.get_value(
		"Account",
		{"is_group": 1, "root_type": root_type, "account_name": account_name, "company": company_name},
	)

	if tax_group_name:
		return tax_group_name

	# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
	# below the root account
	root_account = frappe.get_all(
		"Account",
		{
			"is_group": 1,
			"root_type": root_type,
			"company": company_name,
			"report_type": "Balance Sheet",
			"parent_account": ("is", "not set"),
		},
		limit=1,
	)[0]

	tax_group_account = frappe.get_doc(
		{
			"doctype": "Account",
			"company": company_name,
			"is_group": 1,
			"report_type": "Balance Sheet",
			"root_type": root_type,
			"account_type": "Tax",
			"account_name": account_name,
			"parent_account": root_account.name,
		}
	)

	tax_group_account.flags.ignore_links = True
	tax_group_account.flags.ignore_validate = True
	tax_group_account.insert(ignore_permissions=True, ignore_if_duplicate=True)

	tax_group_name = tax_group_account.name

	return tax_group_name


def make_tax_category(tax_category):
	doctype = "Tax Category"
	if isinstance(tax_category, str):
		tax_category = {"title": tax_category}

	tax_category["doctype"] = doctype
	if not frappe.db.exists(doctype, tax_category["title"]):
		doc = frappe.get_doc(tax_category)
		doc.insert(ignore_permissions=True)
