# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt

import frappe
from frappe.core.doctype.user_permission.test_user_permission import create_user
from frappe.permissions import add_user_permission
from frappe.utils import flt

from erpnext.manufacturing.report.exponential_smoothing_forecasting.exponential_smoothing_forecasting import (
	execute,
)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.tests.utils import ERPNextTestSuite

FROM_DATE = "2026-06-01"
TO_DATE = "2026-08-31"
SMOOTHING_CONSTANT = 0.5


class TestExponentialSmoothingForecasting(ERPNextTestSuite):
	"""Drive real submitted Sales Orders and assert the report buckets the ordered
	quantities into the correct historical periods and produces a forecast."""

	def setUp(self):
		# The forecast query has no lower date bound, so it would pick up any committed
		# Sales Order for the item. A uniquely-named item keeps the buckets scoped to
		# just this test's orders.
		self.item = make_item(properties={"is_stock_item": 1}).name

	def test_future_forecast_stays_flat(self):
		self.create_sales_orders({"2026-03-15": 100, "2026-04-15": 100, "2026-05-15": 100})

		_columns, row = self.run_report(smoothing_constant=0.3)

		for month in ("jun_2026", "jul_2026", "aug_2026"):
			self.assertAlmostEqual(flt(row.get(f"forecast_{month}")), 100, places=6)

	def test_report_follows_document_permissions(self):
		self.create_sales_orders({"2026-05-15": 10})

		stock_user = create_user("forecast-stock-user@example.com", "Stock User")
		with self.set_user(stock_user.name):
			self.assertRaises(
				frappe.PermissionError, execute, self.get_filters(based_on_document="Sales Invoice")
			)

		sales_user = create_user("forecast-sales-user@example.com", "Sales User")
		add_user_permission("Customer", "_Test Customer 1", sales_user.name)
		with self.set_user(sales_user.name):
			data = execute(self.get_filters())[1]
		self.assertFalse([row for row in data if row.get("item_code") == self.item])

	def test_select_only_access_does_not_show_invoice_figures(self):
		from frappe.permissions import add_permission, update_permission_property

		role = "Forecast Invoice Select Only"
		if not frappe.db.exists("Role", role):
			frappe.get_doc({"doctype": "Role", "role_name": role}).insert()
		add_permission("Sales Invoice", role)
		update_permission_property("Sales Invoice", role, 0, "select", 1)
		update_permission_property("Sales Invoice", role, 0, "read", 0)
		user = create_user("forecast-select-only@example.com", "Stock User", role)

		with self.set_user(user.name):
			self.assertRaises(
				frappe.PermissionError, execute, self.get_filters(based_on_document="Sales Invoice")
			)

	def create_sales_orders(self, date_to_qty):
		for transaction_date, qty in date_to_qty.items():
			make_sales_order(item_code=self.item, qty=qty, transaction_date=transaction_date)

	def run_report(self, **extra):
		columns, data = execute(self.get_filters(**extra))[:2]
		item_row = next(
			(r for r in data if r.get("item_code") == self.item),
			None,
		)
		self.assertIsNotNone(item_row, f"{self.item} row missing from report output")
		return columns, item_row

	def get_filters(self, **extra):
		filters = frappe._dict(
			{
				"company": "_Test Company",
				"based_on_document": "Sales Order",
				"based_on_field": "Qty",
				"no_of_years": 3,
				"periodicity": "Monthly",
				"from_date": FROM_DATE,
				"to_date": TO_DATE,
				"smoothing_constant": SMOOTHING_CONSTANT,
				"item_code": self.item,
			}
		)
		filters.update(extra)
		return filters
