import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import now_datetime, getdate, flt, add_months, get_last_day, get_first_day, cint
from datetime import date
from calendar import monthrange


QUARTER_MAP = {
	"Q1 (Jan-Mar)": (1, 3),
	"Q2 (Apr-Jun)": (4, 6),
	"Q3 (Jul-Sep)": (7, 9),
	"Q4 (Oct-Dec)": (10, 12),
}

MONTH_MAP = {
	"January": 1, "February": 2, "March": 3, "April": 4,
	"May": 5, "June": 6, "July": 7, "August": 8,
	"September": 9, "October": 10, "November": 11, "December": 12,
}

TAX_PERIOD_CONFIG = {
	"VAT (ƏDV)": {"entry_period": "Monthly", "declaration_period": "Monthly"},
	"Profit Tax (Mənfəət)": {"entry_period": "Quarterly", "declaration_period": "Annually"},
	"Simplified Tax (Sadələşdirilmiş)": {"entry_period": "Quarterly", "declaration_period": "Quarterly"},
	"Property Tax (Əmlak)": {"entry_period": "Quarterly", "declaration_period": "Annually"},
	"Land Tax (Torpaq)": {"entry_period": "Quarterly", "declaration_period": "Annually"},
	"Excise Tax (Aksiz)": {"entry_period": "Monthly", "declaration_period": "Monthly"},
	"Mining Tax (Mədən)": {"entry_period": "Monthly", "declaration_period": "Monthly"},
	"Road Tax (Yol)": {"entry_period": "Annually", "declaration_period": "Annually"},
	"Withholding Tax (ÖMV)": {"entry_period": "Monthly", "declaration_period": "Monthly"},
	"Unemployment Insurance (İşsizlik)": {"entry_period": "Monthly", "declaration_period": "Quarterly"},
	"DSMF": {"entry_period": "Monthly", "declaration_period": "Monthly"},
	"Medical Insurance (Tibbi sığorta)": {"entry_period": "Monthly", "declaration_period": "Monthly"},
}

DATA_ONLY_TAXES = [
	"Withholding Tax (ÖMV)",
	"Unemployment Insurance (İşsizlik)",
	"DSMF",
	"Medical Insurance (Tibbi sığorta)",
]


class TaxPeriodClosingWizard(Document):
	def validate(self):
		self.compute_dates()
		if not self.cost_center:
			self.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")

	def compute_dates(self):
		fy = frappe.get_doc("Fiscal Year", self.fiscal_year)
		year = getdate(fy.year_start_date).year

		if self.period_type == "Monthly" and self.month:
			m = MONTH_MAP.get(self.month, 1)
			self.from_date = date(year, m, 1)
			self.to_date = date(year, m, monthrange(year, m)[1])

		elif self.period_type == "Quarterly" and self.quarter:
			start_m, end_m = QUARTER_MAP.get(self.quarter, (1, 3))
			self.from_date = date(year, start_m, 1)
			self.to_date = date(year, end_m, monthrange(year, end_m)[1])

		elif self.period_type == "Annually":
			self.from_date = getdate(fy.year_start_date)
			self.to_date = getdate(fy.year_end_date)

	def before_submit(self):
		if self.wizard_status not in ("Entries Created", "Completed"):
			frappe.throw(_("Please run Calculate and Create Entries before submitting."))

	def on_cancel(self):
		self._cancel_all_journal_entries()

	def _cancel_all_journal_entries(self):
		cancelled = []
		for row in self.journal_entries or []:
			if row.journal_entry and frappe.db.exists("Journal Entry", row.journal_entry):
				je = frappe.get_doc("Journal Entry", row.journal_entry)
				if je.docstatus == 1:
					je.cancel()
					cancelled.append(row.journal_entry)
		if cancelled:
			self.add_log("Summary", "Warning",
				f"Cancelled {len(cancelled)} journal entries: {', '.join(cancelled)}")

	# ── Logging ─────────────────────────────────────────────

	def add_log(self, phase, level, message):
		self.append("log_entries", {
			"timestamp": now_datetime(),
			"phase": phase,
			"level": level,
			"message": message,
		})

	def _emit_progress(self, percent, phase, message, level="Info"):
		"""Push live progress to the browser via websocket."""
		self.progress_percent = percent
		frappe.publish_realtime(
			"tpcw_progress",
			{
				"percent": percent,
				"phase": phase,
				"message": message,
				"level": level,
				"name": self.name,
			},
			doctype=self.doctype,
			docname=self.name,
			after_commit=False,
		)

	# ── Phase 1: Initialization ──────────────────────────────

	def detect_company_info(self):
		self._emit_progress(5, "Initialization", "Loading company data...")
		company = frappe.get_doc("Company", self.company)
		self.company_voen = company.tax_id or ""
		self.company_activity_code = ""
		self.company_classification = ""
		self.company_tax_authority = ""

		if hasattr(company, "main_activity") and company.main_activity:
			self.company_activity_code = company.main_activity
		if hasattr(company, "business_classification") and company.business_classification:
			self.company_classification = company.business_classification
		if hasattr(company, "tax_authority") and company.tax_authority:
			self.company_tax_authority = company.tax_authority

		self.is_vat_payer = self._check_vat_payer()
		self.has_assets = self._check_has_assets()
		self.has_land = 0  # Land checking would need custom fields on Asset
		self.has_employees = self._check_has_employees()

		self.add_log("Initialization", "Info",
			f"Company: {self.company} | VOEN: {self.company_voen} | "
			f"OKVED: {self.company_activity_code} | "
			f"Classification: {self.company_classification}")
		self.add_log("Initialization", "Info",
			f"Period: {self.from_date} — {self.to_date} | "
			f"Fiscal Year: {self.fiscal_year} | Currency: {company.default_currency}")

	def _check_vat_payer(self):
		settings = self._get_settings()
		if settings.vat_output_account:
			return 1
		# Also check if company has VAT certificate
		company = frappe.get_doc("Company", self.company)
		if hasattr(company, "vat_certificate_number") and company.vat_certificate_number:
			return 1
		return 0

	def _check_has_assets(self):
		count = frappe.db.count("Asset", {
			"company": self.company,
			"docstatus": 1,
			"status": ["not in", ["Scrapped", "Sold"]],
		})
		return 1 if count > 0 else 0

	def _check_has_employees(self):
		count = frappe.db.count("Employee", {
			"company": self.company,
			"status": "Active",
		})
		return 1 if count > 0 else 0

	# ── Phase 1.5: Benefit Detection ─────────────────────────

	def detect_benefits(self):
		"""Auto-detect applicable tax benefits based on AZ Tax Code."""
		self._emit_progress(8, "Benefit Detection", "Scanning tax benefits...")
		self.add_log("Tax Detection", "Header", "Detecting tax benefits & exemptions...")
		self.detected_benefits = []

		classification = self.company_classification or ""
		is_micro = "Micro" in classification
		is_small = "Small" in classification or "Kiçik" in classification
		is_micro_or_small = is_micro or is_small

		# Get actual data for condition checks
		year = getdate(self.from_date).year
		avg_employees = self._get_avg_monthly_employees(year)
		annual_revenue = self._get_annual_revenue(year)
		has_startup_cert = self._check_startup_certificate()
		has_investment_cert = self._check_investment_certificate()

		self.add_log("Tax Detection", "Info",
			f"Classification: {classification} | Avg employees: {avg_employees} | "
			f"Annual revenue: {annual_revenue:,.2f} | Startup cert: {has_startup_cert}")

		# ── Maddə 106.1.20 — Micro: 75% profit exemption ──
		if is_micro:
			employees_ok = avg_employees >= 3
			# No DSMF debt check automated — mark as Manual Check
			cond = "Yes" if employees_ok else "No"
			details = (f"Avg employees: {avg_employees} (need ≥3). "
				f"DSMF debt: requires manual verification.")
			if not employees_ok:
				details += " CONDITION NOT MET: less than 3 employees."

			self.append("detected_benefits", {
				"enabled": 1 if employees_ok else 0,
				"article": "Maddə 106.1.20",
				"benefit_name": "Mikro sahibkar — 75% profit exemption",
				"applies_to": "Profit Tax (Mənfəət)",
				"exemption_percent": 75,
				"condition_met": cond if employees_ok else "No",
				"condition_details": details,
				"law_reference": "Vergi Məcəlləsi, Maddə 106.1.20 — Mikro sahibkarlıq subyekti olan hüquqi şəxslərin müvafiq il üzrə orta aylıq muzdlu işçi sayı 3 nəfərdən az olmayan və məcburi dövlət sosial sığorta haqları üzrə borcu olmayanların sahibkarlıq fəaliyyətindən əldə etdikləri mənfəətin 75 faizi vergidən azaddır.",
				"law_url": "https://e-qanun.az/framework/46947",
			})
			level = "Success" if employees_ok else "Warning"
			self.add_log("Tax Detection", level,
				f"Maddə 106.1.20: Micro 75% profit exemption — "
				f"{'APPLICABLE' if employees_ok else 'NOT APPLICABLE'} "
				f"(employees: {avg_employees}/3)")

		# ── Maddə 106.1.23 — Startup (micro/small): 100% profit exemption ──
		if is_micro_or_small and has_startup_cert:
			self.append("detected_benefits", {
				"enabled": 1,
				"article": "Maddə 106.1.23",
				"benefit_name": "Startap — 100% profit exemption",
				"applies_to": "Profit Tax (Mənfəət)",
				"exemption_percent": 100,
				"condition_met": "Yes",
				"condition_details": "Startup certificate detected. 100% exemption on innovation profit.",
				"law_reference": "Vergi Məcəlləsi, Maddə 106.1.23 — Mikro və ya kiçik sahibkarlıq subyekti olan və hüquqi şəxs kimi fəaliyyət göstərən startap şəhadətnaməsinə malik müəssisələrin innovasiya fəaliyyətindən əldə etdikləri mənfəəti vergidən azaddır.",
				"law_url": "https://e-qanun.az/framework/46947",
			})
			self.add_log("Tax Detection", "Success",
				"Maddə 106.1.23: Startup 100% profit exemption — APPLICABLE")

		# ── Maddə 221.1-1 — Micro simplified: 25% DSMF reduction ──
		if is_micro:
			regime = self.tax_regime or ""
			if "Simplified" in regime or "Special" in regime:
				self.append("detected_benefits", {
					"enabled": 1,
					"article": "Maddə 221.1-1",
					"benefit_name": "Mikro — 25% quarterly DSMF offset against simplified tax",
					"applies_to": "Simplified Tax (Sadələşdirilmiş)",
					"exemption_percent": 25,
					"condition_met": "Yes",
					"condition_details": "Micro entrepreneur on simplified regime. 25% of quarterly DSMF contributions offset against simplified tax.",
					"law_reference": "Vergi Məcəlləsi, Maddə 221.1-1 — Mikro sahibkarlıq subyekti olan fiziki şəxslərin sadələşdirilmiş vergi üzrə rüblük məcburi dövlət sosial sığorta haqqı məbləğinin 25 faizi sadələşdirilmiş vergidən çıxılır.",
					"law_url": "https://e-qanun.az/framework/46947",
				})
				self.add_log("Tax Detection", "Success",
					"Maddə 221.1-1: Micro 25% DSMF offset — APPLICABLE")

		# ── Maddə 106.1.17 — Investment promotion: 50% profit exemption ──
		if has_investment_cert:
			self.append("detected_benefits", {
				"enabled": 1,
				"article": "Maddə 106.1.17",
				"benefit_name": "Investment promotion — 50% profit exemption",
				"applies_to": "Profit Tax (Mənfəət)",
				"exemption_percent": 50,
				"condition_met": "Yes",
				"condition_details": "Investment promotion certificate detected.",
				"law_reference": "Vergi Məcəlləsi, Maddə 106.1.17 — İnvestisiya təşviqi sənədini almış hüquqi şəxslərin həmin sənədi aldığı tarixdən əldə etdiyi mənfəətin 50 faizi vergidən azaddır.",
				"law_url": "https://e-qanun.az/framework/46947",
			})
			self.add_log("Tax Detection", "Success",
				"Maddə 106.1.17: Investment 50% profit exemption — APPLICABLE")

		# ── Maddə 106.10 — Dividend exemption (revenue < 200,000) ──
		if annual_revenue <= 200000 and not self.is_vat_payer:
			self.append("detected_benefits", {
				"enabled": 1,
				"article": "Maddə 106.10",
				"benefit_name": "Dividend income exemption (turnover ≤ 200,000)",
				"applies_to": "Profit Tax (Mənfəət)",
				"exemption_percent": 100,
				"condition_met": "Yes",
				"condition_details": f"Annual revenue {annual_revenue:,.2f} ≤ 200,000 and not VAT payer. Dividend income is 100% exempt.",
				"law_reference": "Vergi Məcəlləsi, Maddə 106.10 — Əməliyyatlarının həcmi ardıcıl 12 aylıq dövrün istənilən ayında 200.000 manatadək olan rezident müəssisənin ƏDV-nin ödəyicisi kimi qeydiyyatda olmayan vergi ödəyicisinin dividend gəliri vergidən azaddır.",
				"law_url": "https://e-qanun.az/framework/46947",
			})
			self.add_log("Tax Detection", "Success",
				f"Maddə 106.10: Dividend exemption — APPLICABLE (revenue {annual_revenue:,.2f} ≤ 200,000)")

		# ── Maddə 199.4 — Property tax exemptions (auto-detect from Asset) ──
		if self.has_assets:
			exempt_assets = frappe.db.sql("""
				SELECT COUNT(*) as cnt
				FROM `tabAsset`
				WHERE company = %s AND docstatus = 1
				AND status NOT IN ('Scrapped', 'Sold')
				AND (tax_exempt_tax_article IS NOT NULL AND tax_exempt_tax_article != '')
			""", self.company, as_dict=True)
			exempt_count = exempt_assets[0].cnt if exempt_assets else 0
			if exempt_count > 0:
				self.append("detected_benefits", {
					"enabled": 1,
					"article": "Maddə 199.4",
					"benefit_name": f"Property tax exempt assets ({exempt_count} units)",
					"applies_to": "Property Tax (Əmlak)",
					"exemption_percent": 100,
					"condition_met": "Yes",
					"condition_details": f"{exempt_count} assets marked as tax-exempt in Asset register.",
					"law_reference": "Vergi Məcəlləsi, Maddə 199.4 — Əmlak vergisindən azad olunan əsas vəsaitlərin siyahısı. Maddə 199.4.1–199.4.4 ilə müəyyən edilən kateqoriyalara aid əsas vəsaitlər vergidən azaddır.",
					"law_url": "https://e-qanun.az/framework/46947",
				})
				self.add_log("Tax Detection", "Success",
					f"Maddə 199.4: {exempt_count} tax-exempt assets found")

		# ── DSMF rates for micro (15% instead of 22%) ──
		if is_micro:
			self.append("detected_benefits", {
				"enabled": 1,
				"article": "DSMF Micro Rate",
				"benefit_name": "Mikro — DSMF employer rate 15% (instead of 22%)",
				"applies_to": "DSMF",
				"exemption_percent": 32,
				"condition_met": "Yes",
				"condition_details": "Micro entrepreneur DSMF employer rate is 15% instead of standard 22% (difference 7 p.p., ~32% reduction). Applied in payroll, shown here for reference — verify Payroll settings.",
				"law_reference": "\"Sosial sığorta haqqında\" Azərbaycan Respublikasının Qanunu — Mikro sahibkarlıq subyektləri üçün işəgötürənin məcburi dövlət sosial sığorta haqqı dərəcəsi 15%-dir (standart 22% əvəzinə).",
				"law_url": "https://e-qanun.az/framework/46854",
			})
			self.add_log("Tax Detection", "Info",
				"DSMF Micro Rate: 15% employer (32% reduction from standard 22%) — verify in Payroll")

		if not self.detected_benefits:
			self.add_log("Tax Detection", "Info",
				"No special tax benefits detected for this company.")

	def _get_avg_monthly_employees(self, year):
		"""Get average monthly employee count for the year."""
		# Count employees who were active during the year
		total = frappe.db.sql("""
			SELECT COUNT(DISTINCT e.name) as cnt
			FROM `tabEmployee` e
			WHERE e.company = %s
			AND e.status = 'Active'
			AND (e.date_of_joining IS NULL OR e.date_of_joining <= %s)
			AND (e.relieving_date IS NULL OR e.relieving_date >= %s)
		""", (self.company, f"{year}-12-31", f"{year}-01-01"), as_dict=True)
		count = total[0].cnt if total else 0

		# For monthly average we need salary slips
		monthly_counts = frappe.db.sql("""
			SELECT MONTH(posting_date) as month, COUNT(DISTINCT employee) as cnt
			FROM `tabSalary Slip`
			WHERE company = %s AND docstatus = 1
			AND posting_date BETWEEN %s AND %s
			GROUP BY MONTH(posting_date)
		""", (self.company, f"{year}-01-01", f"{year}-12-31"), as_dict=True)

		if monthly_counts:
			avg = sum(m.cnt for m in monthly_counts) / len(monthly_counts)
			return round(avg, 1)
		return count

	def _get_annual_revenue(self, year):
		"""Get total revenue for the year from GL."""
		from erpnext.accounts.utils import get_balance_on

		income_accounts = frappe.get_all("Account", filters={
			"company": self.company,
			"root_type": "Income",
			"is_group": 0,
		}, pluck="name")

		total = 0
		for acc in income_accounts:
			bal = flt(get_balance_on(acc, f"{year}-12-31", company=self.company,
				start_date=f"{year}-01-01"))
			total += bal
		return abs(total)

	def _check_startup_certificate(self):
		"""Check if company has a startup certificate."""
		company = frappe.get_doc("Company", self.company)
		# Check for custom field or certificate data
		if hasattr(company, "startup_certificate") and company.startup_certificate:
			return True
		# Check certificate_data child table
		for cert in getattr(company, "certificate_data", []):
			if "startap" in (cert.get("certificate_type") or "").lower():
				return True
			if "startup" in (cert.get("certificate_type") or "").lower():
				return True
		return False

	def _check_investment_certificate(self):
		"""Check if company has an investment promotion certificate."""
		company = frappe.get_doc("Company", self.company)
		for cert in getattr(company, "certificate_data", []):
			if "investisiya" in (cert.get("certificate_type") or "").lower():
				return True
			if "investment" in (cert.get("certificate_type") or "").lower():
				return True
		return False

	# ── Phase 2: Tax Detection ────────────────────────────────

	def detect_taxes(self):
		self._emit_progress(10, "Tax Detection", "Determining applicable taxes...")
		settings = self._get_settings()
		regime = self.tax_regime or settings.default_tax_regime or "General (VAT + Profit)"

		self.add_log("Tax Detection", "Header",
			f"Tax regime: {regime}")

		self.tax_items = []

		if regime == "General (VAT + Profit)":
			if self.is_vat_payer:
				self._add_tax_items_for_period("VAT (ƏDV)")
			self._add_tax_items_for_period("Profit Tax (Mənfəət)")
		elif regime == "Simplified Tax":
			self._add_tax_items_for_period("Simplified Tax (Sadələşdirilmiş)")
		elif regime == "Special Regime":
			self._add_tax_items_for_period("Simplified Tax (Sadələşdirilmiş)")

		if self.has_assets:
			self._add_tax_items_for_period("Property Tax (Əmlak)")
		if self.has_land:
			self._add_tax_items_for_period("Land Tax (Torpaq)")

		# Data-only taxes
		if self.has_employees:
			for tax in DATA_ONLY_TAXES:
				self._add_tax_items_for_period(tax)

	def _add_tax_items_for_period(self, tax_type):
		config = TAX_PERIOD_CONFIG.get(tax_type, {})
		entry_period = config.get("entry_period", "Quarterly")

		is_data_only = tax_type in DATA_ONLY_TAXES

		if entry_period == "Monthly":
			months = self._get_months_in_range()
			for m_num, m_name in months:
				year = getdate(self.from_date).year
				sub = f"{m_name} {year}"
				deadlines = self._get_deadlines(tax_type, year, m_num)
				status = "Data Only" if is_data_only else "Pending"
				self.append("tax_items", {
					"enabled": 1,
					"tax_type": tax_type,
					"period_type": "Monthly",
					"sub_period": sub,
					"status": status,
					"declaration_deadline": deadlines.get("declaration"),
					"payment_deadline": deadlines.get("payment"),
				})
				action = "DATA ONLY" if is_data_only else "INCLUDED"
				self.add_log("Tax Detection", "Success",
					f"{tax_type} — {action} ({sub})")
		elif entry_period == "Quarterly":
			# Add one item for the whole selected period
			year = getdate(self.from_date).year
			sub = self._get_period_label()
			deadlines = self._get_deadlines(tax_type, year, getdate(self.to_date).month)
			self.append("tax_items", {
				"enabled": 1,
				"tax_type": tax_type,
				"period_type": "Quarterly",
				"sub_period": sub,
				"status": "Pending",
				"declaration_deadline": deadlines.get("declaration"),
				"payment_deadline": deadlines.get("payment"),
			})
			self.add_log("Tax Detection", "Success",
				f"{tax_type} — INCLUDED ({sub})")
		elif entry_period == "Annually":
			year = getdate(self.from_date).year
			sub = str(year)
			deadlines = self._get_deadlines(tax_type, year, 12)
			self.append("tax_items", {
				"enabled": 1,
				"tax_type": tax_type,
				"period_type": "Annually",
				"sub_period": sub,
				"status": "Pending",
				"declaration_deadline": deadlines.get("declaration"),
				"payment_deadline": deadlines.get("payment"),
			})
			self.add_log("Tax Detection", "Success",
				f"{tax_type} — INCLUDED ({sub})")

	def _get_months_in_range(self):
		from_d = getdate(self.from_date)
		to_d = getdate(self.to_date)
		months = []
		current = from_d
		while current <= to_d:
			m_name = list(MONTH_MAP.keys())[current.month - 1]
			months.append((current.month, m_name))
			if current.month == 12:
				break
			next_month = current.month + 1
			current = date(current.year, next_month, 1)
		return months

	def _get_period_label(self):
		if self.period_type == "Monthly" and self.month:
			year = getdate(self.from_date).year
			return f"{self.month} {year}"
		elif self.period_type == "Quarterly" and self.quarter:
			year = getdate(self.from_date).year
			return f"{self.quarter} {year}"
		elif self.period_type == "Annually":
			return str(getdate(self.from_date).year)
		return str(self.from_date)

	def _get_deadlines(self, tax_type, year, month):
		# Declaration and payment deadlines per AZ Tax Code
		next_m = month + 1
		next_y = year
		if next_m > 12:
			next_m = next_m - 12
			next_y += 1

		if tax_type == "VAT (ƏDV)":
			return {
				"declaration": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
				"payment": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
			}
		elif tax_type == "Profit Tax (Mənfəət)":
			return {
				"declaration": date(year + 1, 3, 31),
				"payment": date(next_y, next_m, min(15, monthrange(next_y, next_m)[1])),
			}
		elif tax_type == "Simplified Tax (Sadələşdirilmiş)":
			return {
				"declaration": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
				"payment": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
			}
		elif tax_type in ("Property Tax (Əmlak)", "Land Tax (Torpaq)"):
			return {
				"declaration": date(year + 1, 3, 31),
				"payment": date(next_y, next_m, min(15, monthrange(next_y, next_m)[1])),
			}
		elif tax_type in ("Withholding Tax (ÖMV)", "DSMF", "Medical Insurance (Tibbi sığorta)"):
			return {
				"declaration": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
				"payment": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
			}
		elif tax_type == "Unemployment Insurance (İşsizlik)":
			return {
				"declaration": date(next_y, next_m, min(20, monthrange(next_y, next_m)[1])),
				"payment": date(next_y, next_m, min(15, monthrange(next_y, next_m)[1])),
			}
		return {"declaration": None, "payment": None}

	# ── Phase 3: Data Collection ──────────────────────────────

	def collect_financial_data(self):
		self.add_log("Data Collection", "Header", "Collecting financial data...")

		self._emit_progress(20, "Data Collection", "Reading P&L accounts...")
		self._collect_pl_data()

		self._emit_progress(30, "Data Collection", "Reading VAT accounts...")
		self._collect_vat_data()

		self._emit_progress(40, "Data Collection", "Reading asset register...")
		self._collect_asset_data()

		self._emit_progress(48, "Data Collection", "Reading payroll data...")
		self._collect_payroll_data()

	def _collect_pl_data(self):
		from erpnext.accounts.utils import get_balance_on

		company = self.company
		from_d = str(self.from_date)
		to_d = str(self.to_date)

		# Get income totals
		income_accounts = frappe.get_all("Account", filters={
			"company": company,
			"root_type": "Income",
			"is_group": 0,
		}, pluck="name")

		total_income = 0
		for acc in income_accounts:
			bal = flt(get_balance_on(acc, to_d, company=company, start_date=from_d))
			total_income += bal

		# Income accounts have credit balance, so it's negative in get_balance_on
		self.total_income = abs(total_income)

		# Get expense totals
		expense_accounts = frappe.get_all("Account", filters={
			"company": company,
			"root_type": "Expense",
			"is_group": 0,
		}, pluck="name")

		total_expense = 0
		for acc in expense_accounts:
			bal = flt(get_balance_on(acc, to_d, company=company, start_date=from_d))
			total_expense += bal

		self.total_expense = abs(total_expense)
		self.net_profit = self.total_income - self.total_expense

		self.add_log("Data Collection", "Info",
			f"Total Income: {self.total_income:,.2f} AZN")
		self.add_log("Data Collection", "Info",
			f"Total Expense: {self.total_expense:,.2f} AZN")

		profit_level = "Success" if self.net_profit > 0 else "Warning"
		self.add_log("Data Collection", profit_level,
			f"Net Profit/Loss: {self.net_profit:,.2f} AZN")

	def _collect_vat_data(self):
		if not self.is_vat_payer:
			return

		settings = self._get_settings()
		vat_input = settings.vat_input_account
		vat_output = settings.vat_output_account

		if not vat_input or not vat_output:
			self.add_log("Data Collection", "Warning",
				"VAT accounts not configured in Settings. Skipping VAT data collection.")
			return

		from erpnext.accounts.utils import get_balance_on

		months = self._get_months_in_range()
		for m_num, m_name in months:
			year = getdate(self.from_date).year
			m_start = date(year, m_num, 1)
			m_end = date(year, m_num, monthrange(year, m_num)[1])

			# Output VAT (credit balance = liability)
			output_bal = abs(flt(get_balance_on(
				vat_output, str(m_end), company=self.company, start_date=str(m_start))))
			# Input VAT (debit balance = asset)
			input_bal = abs(flt(get_balance_on(
				vat_input, str(m_end), company=self.company, start_date=str(m_start))))

			self.add_log("Data Collection", "Info",
				f"VAT {m_name} {year}: Output={output_bal:,.2f} | "
				f"Input={input_bal:,.2f} | Net={output_bal - input_bal:,.2f}")

	# Asset categories exempt from property tax per Maddə 199.4
	EXEMPT_ASSET_CATEGORIES = [
		"Yüksək texnologiyalar məhsulu olan hesablama texnikası",  # 199.4.4
	]

	def _collect_asset_data(self):
		if not self.has_assets:
			self.add_log("Data Collection", "Info", "No fixed assets found.")
			return

		assets = frappe.get_all("Asset", filters={
			"company": self.company,
			"docstatus": 1,
			"status": ["not in", ["Scrapped", "Sold"]],
		}, fields=["name", "asset_name", "total_asset_cost", "value_after_depreciation",
			"opening_accumulated_depreciation", "taxable_asset_type",
			"tax_exempt_tax_article", "asset_category"])

		total_gross = 0
		total_dep = 0
		taxable_nbv = 0
		exempt_nbv = 0
		exempt_count = 0

		for asset in assets:
			gross = flt(asset.total_asset_cost)
			nbv = flt(asset.value_after_depreciation)
			total_gross += gross
			total_dep += (gross - nbv)

			# Check if asset is exempt
			is_exempt = False
			exempt_reason = ""

			# 1. Explicit exemption field
			if asset.tax_exempt_tax_article:
				is_exempt = True
				exempt_reason = f"Maddə {asset.tax_exempt_tax_article}"

			# 2. Auto-detect by asset category (Maddə 199.4.4)
			elif asset.asset_category in self.EXEMPT_ASSET_CATEGORIES:
				is_exempt = True
				exempt_reason = "Maddə 199.4.4 (auto: hesablama texnikası)"

			# 3. Auto-detect by taxable_asset_type
			elif asset.taxable_asset_type in self.EXEMPT_ASSET_CATEGORIES:
				is_exempt = True
				exempt_reason = "Maddə 199.4.4 (auto: hesablama texnikası)"

			if is_exempt:
				exempt_nbv += nbv
				exempt_count += 1
				self.add_log("Data Collection", "Success",
					f"  EXEMPT: {asset.asset_name} | NBV: {nbv:,.2f} | {exempt_reason}")
			else:
				taxable_nbv += nbv
				self.add_log("Data Collection", "Info",
					f"  TAXABLE: {asset.asset_name} | NBV: {nbv:,.2f}")

		self.total_asset_value = total_gross
		self.accumulated_depreciation = total_dep
		self.net_book_value = total_gross - total_dep

		# Store taxable NBV for property tax calculation (not total NBV)
		self.avg_annual_value = taxable_nbv

		asset_count = len(assets)
		self.add_log("Data Collection", "Info",
			f"Fixed Assets: {asset_count} total | "
			f"Gross: {total_gross:,.2f} | NBV: {self.net_book_value:,.2f}")
		self.add_log("Data Collection", "Info",
			f"  Taxable NBV: {taxable_nbv:,.2f} ({asset_count - exempt_count} assets) | "
			f"Exempt NBV: {exempt_nbv:,.2f} ({exempt_count} assets)")

		if exempt_count > 0:
			self.add_log("Data Collection", "Success",
				f"  {exempt_count} asset(s) exempt from property tax per Maddə 199.4")

		if taxable_nbv == 0 and asset_count > 0:
			self.add_log("Data Collection", "Success",
				"  All assets are TAX EXEMPT — Property Tax = 0")

		settings = self._get_settings()
		threshold = flt(settings.property_tax_threshold) or 1000000
		if taxable_nbv > threshold:
			self.add_log("Data Collection", "Warning",
				f"  Taxable NBV exceeds threshold {threshold:,.2f} AZN (Maddə 199)")
		elif taxable_nbv > 0:
			self.add_log("Data Collection", "Info",
				f"  Taxable NBV within threshold {threshold:,.2f} AZN")

	def _collect_payroll_data(self):
		if not self.has_employees:
			self.add_log("Data Collection", "Info", "No active employees found.")
			return

		self.payroll_summary = []

		# Get salary slips for the period
		salary_slips = frappe.get_all("Salary Slip", filters={
			"company": self.company,
			"posting_date": ["between", [str(self.from_date), str(self.to_date)]],
			"docstatus": 1,
		}, fields=["name", "gross_pay", "total_deduction", "net_pay"])

		if not salary_slips:
			self.add_log("Data Collection", "Warning",
				"No submitted salary slips found for this period.")
			return

		total_gross = sum(flt(s.gross_pay) for s in salary_slips)
		total_deduction = sum(flt(s.total_deduction) for s in salary_slips)
		total_net = sum(flt(s.net_pay) for s in salary_slips)

		self.append("payroll_summary", {
			"component": _("Gross Pay"),
			"amount": total_gross,
			"source": f"{len(salary_slips)} Salary Slips",
		})
		self.append("payroll_summary", {
			"component": "Total Deductions",
			"amount": total_deduction,
			"source": "Salary Slips",
		})
		self.append("payroll_summary", {
			"component": "Net Pay",
			"amount": total_net,
			"source": "Salary Slips",
		})

		# Collect deduction components
		deduction_totals = frappe.db.sql("""
			SELECT sd.salary_component, SUM(sd.amount) as total
			FROM `tabSalary Detail` sd
			JOIN `tabSalary Slip` ss ON ss.name = sd.parent
			WHERE ss.company = %s
			AND ss.posting_date BETWEEN %s AND %s
			AND ss.docstatus = 1
			AND sd.parentfield = 'deductions'
			GROUP BY sd.salary_component
		""", (self.company, str(self.from_date), str(self.to_date)), as_dict=True)

		total_payroll_taxes = 0
		for d in deduction_totals:
			self.append("payroll_summary", {
				"component": d.salary_component,
				"amount": flt(d.total),
				"source": "Salary Detail",
			})
			total_payroll_taxes += flt(d.total)

		self.total_payroll_taxes = total_payroll_taxes

		self.add_log("Data Collection", "Info",
			f"Payroll: Gross={total_gross:,.2f} | "
			f"Deductions={total_deduction:,.2f} | Net={total_net:,.2f}")
		self.add_log("Data Collection", "Info",
			f"Payroll tax components: {len(deduction_totals)}")

	# ── Phase 4: Calculate Taxes ──────────────────────────────

	def calculate_taxes(self):
		self._emit_progress(52, "Calculation", "Calculating tax amounts...")
		self.add_log("Calculation", "Header", "Calculating taxes...")
		settings = self._get_settings()

		for item in self.tax_items:
			if not item.enabled:
				item.status = "Skipped"
				self.add_log("Calculation", "Info",
					f"{item.tax_type} ({item.sub_period}) — SKIPPED (disabled)")
				continue

			if item.tax_type in DATA_ONLY_TAXES:
				item.status = "Data Only"
				continue

			try:
				self._calculate_single_tax(item, settings)
				if item.status != "Error":
					item.status = "Calculated"
			except Exception as e:
				item.status = "Error"
				self.add_log("Calculation", "Error",
					f"{item.tax_type} ({item.sub_period}) — ERROR: {str(e)}")
				frappe.log_error(f"Tax calculation error: {str(e)}")
				raise

	def _calculate_single_tax(self, item, settings):
		if item.tax_type == "VAT (ƏDV)":
			self._calc_vat(item, settings)
		elif item.tax_type == "Profit Tax (Mənfəət)":
			self._calc_profit_tax(item, settings)
		elif item.tax_type == "Simplified Tax (Sadələşdirilmiş)":
			self._calc_simplified_tax(item, settings)
		elif item.tax_type == "Property Tax (Əmlak)":
			self._calc_property_tax(item, settings)
		elif item.tax_type == "Land Tax (Torpaq)":
			self._calc_land_tax(item, settings)

	def _calc_vat(self, item, settings):
		from erpnext.accounts.utils import get_balance_on

		vat_input = settings.vat_input_account
		vat_output = settings.vat_output_account
		if not vat_input or not vat_output:
			item.status = "Error"
			self.add_log("Calculation", "Error",
				"VAT accounts not configured. Set them in Tax Period Closing Settings.")
			return

		# Parse month from sub_period
		parts = item.sub_period.split()
		if len(parts) >= 2:
			m_name = parts[0]
			year = int(parts[1])
			m_num = MONTH_MAP.get(m_name, 1)
		else:
			year = getdate(self.from_date).year
			m_num = getdate(self.from_date).month

		m_start = date(year, m_num, 1)
		m_end = date(year, m_num, monthrange(year, m_num)[1])

		output_bal = abs(flt(get_balance_on(
			vat_output, str(m_end), company=self.company, start_date=str(m_start))))
		input_bal = abs(flt(get_balance_on(
			vat_input, str(m_end), company=self.company, start_date=str(m_start))))

		item.source_amount = output_bal
		item.rate = 0  # VAT is net calculation, not rate-based
		item.tax_amount = input_bal  # Amount to offset (input VAT to close)

		net = output_bal - input_bal
		sign = _("payable") if net > 0 else _("refundable")

		self.add_log("Calculation", "Success",
			f"VAT {item.sub_period}: Output={output_bal:,.2f} − Input={input_bal:,.2f} "
			f"= {abs(net):,.2f} ({sign})")

	def _calc_profit_tax(self, item, settings):
		method = settings.profit_tax_advance_method or "Actual Quarterly Profit"
		rate = flt(settings.profit_tax_rate) or 20

		if method == "Actual Quarterly Profit":
			profit = flt(self.net_profit)
			tax_before_benefits = max(0, profit * rate / 100)
		else:
			tax_before_benefits = max(0, flt(self.net_profit) * rate / 100)

		# Apply detected benefits
		total_exemption_pct = 0
		applied_benefits = []
		for benefit in self.detected_benefits or []:
			if (benefit.enabled
				and benefit.applies_to == "Profit Tax (Mənfəət)"
				and benefit.condition_met in ("Yes", "Partial")):
				pct = flt(benefit.exemption_percent)
				total_exemption_pct += pct
				applied_benefits.append(f"{benefit.article}: {pct}%")

		# Cap at 100%
		total_exemption_pct = min(total_exemption_pct, 100)

		tax_reduction = tax_before_benefits * total_exemption_pct / 100
		tax = max(0, tax_before_benefits - tax_reduction)

		# Update benefit rows with calculated reduction
		for benefit in self.detected_benefits or []:
			if (benefit.enabled
				and benefit.applies_to == "Profit Tax (Mənfəət)"
				and benefit.condition_met in ("Yes", "Partial")):
				benefit.tax_reduction = tax_before_benefits * flt(benefit.exemption_percent) / 100

		item.source_amount = flt(self.net_profit)
		item.rate = rate
		item.tax_amount = tax

		self.add_log("Calculation", "Success",
			f"Profit Tax ({item.sub_period}): "
			f"Profit={self.net_profit:,.2f} × {rate}% = {tax_before_benefits:,.2f}")

		if total_exemption_pct > 0:
			self.add_log("Calculation", "Success",
				f"  → Benefits applied: {', '.join(applied_benefits)} "
				f"= {total_exemption_pct}% exemption")
			self.add_log("Calculation", "Success",
				f"  → Tax reduction: {tax_reduction:,.2f} | "
				f"Final tax: {tax:,.2f}")

	def _calc_simplified_tax(self, item, settings):
		rate = flt(settings.default_simplified_rate) or 4
		revenue = flt(self.total_income)
		tax = revenue * rate / 100

		item.source_amount = revenue
		item.rate = rate
		item.tax_amount = tax

		self.add_log("Calculation", "Success",
			f"Simplified Tax ({item.sub_period}): "
			f"Revenue={revenue:,.2f} × {rate}% = {tax:,.2f}")

	def _calc_property_tax(self, item, settings):
		rate = flt(settings.property_tax_rate) or 1
		# avg_annual_value = taxable NBV only (exempt assets excluded)
		taxable_nbv = flt(self.avg_annual_value)  # Can be 0 if all exempt

		if taxable_nbv == 0:
			item.source_amount = 0
			item.rate = rate
			item.tax_amount = 0
			self.add_log("Calculation", "Success",
				f"Property Tax ({item.sub_period}): "
				f"Taxable NBV = 0.00 (all assets exempt per Maddə 199.4) → Tax = 0.00")
			return

		annual_tax = taxable_nbv * rate / 100

		# Quarterly advance = 1/4 of annual
		if item.period_type == "Quarterly":
			tax = annual_tax / 4
		else:
			tax = annual_tax

		item.source_amount = taxable_nbv
		item.rate = rate
		item.tax_amount = tax

		period_label = "1/4 annual" if item.period_type == "Quarterly" else "annual"
		self.add_log("Calculation", "Success",
			f"Property Tax ({item.sub_period}): "
			f"Taxable NBV={taxable_nbv:,.2f} × {rate}% = {annual_tax:,.2f} → "
			f"{period_label} = {tax:,.2f}")

	def _calc_land_tax(self, item, settings):
		# Land tax uses existing logic from land_tax_declaration.py
		item.source_amount = 0
		item.rate = 0
		item.tax_amount = 0
		self.add_log("Calculation", "Info",
			"Land Tax: Uses existing calculation from Land Tax Declaration. "
			"Set amount manually if needed.")

	# ── Phase 5: Generate Entry Previews ──────────────────────

	def generate_previews(self):
		self._emit_progress(70, "Entry Preview", "Scanning existing entries & generating previews...")
		self.add_log("Entry Preview", "Header", "Generating entry previews...")
		self.entry_previews = []
		settings = self._get_settings()
		do_scan = getattr(settings, "scan_existing_entries", True)
		offer_correction = getattr(settings, "offer_correction_entry", True)
		force_create = getattr(settings, "force_create_if_exists", False)

		if do_scan:
			self.add_log("Entry Preview", "Info", "Smart Scan enabled — checking for existing entries...")

		for item in self.tax_items:
			if not item.enabled or item.status in ("Skipped", "Data Only", "Error"):
				continue
			if flt(item.tax_amount) == 0 and item.tax_type != "VAT (ƏDV)":
				self.add_log("Entry Preview", "Warning",
					f"{item.tax_type} ({item.sub_period}) — Amount is 0, skipping.")
				continue

			accounts = self._get_accounts_for_tax(item.tax_type, settings)
			if not accounts:
				self.add_log("Entry Preview", "Error",
					f"No account mapping for {item.tax_type}. Configure in Settings.")
				item.status = "Error"
				continue

			posting_date = self._get_posting_date(item)
			amount = flt(item.tax_amount)

			debit_acc = accounts["debit"]
			credit_acc = accounts["credit"]

			if item.tax_type == "VAT (ƏDV)":
				debit_acc = settings.vat_output_account
				credit_acc = settings.vat_input_account
				if amount <= 0:
					continue

			remark_prefix = _("VAT Input Offset") if item.tax_type == "VAT (ƏDV)" else _("Accrual {0}").format(item.tax_type)

			# ── Smart Scan ──
			existing = None
			if do_scan:
				existing = self._find_existing_entry(debit_acc, credit_acc, posting_date, item)

			if existing and not force_create:
				existing_amount = flt(existing["amount"])
				diff = flt(amount - existing_amount, 2)

				self.append("entry_previews", {
					"tax_type": item.tax_type,
					"sub_period": item.sub_period,
					"posting_date": posting_date,
					"debit_account": debit_acc,
					"credit_account": credit_acc,
					"amount": amount,
					"existing_entry": existing["je_name"],
					"existing_amount": existing_amount,
					"difference": diff,
					"remark": f"Existing: {existing['je_name']} ({existing_amount:,.2f} AZN)",
					"status": "Existing" if abs(diff) < 0.01 else "Difference",
				})

				if abs(diff) < 0.01:
					self.add_log("Entry Preview", "Success",
						f"EXISTING: {item.tax_type} ({item.sub_period}) | "
						f"{existing['je_name']} | {existing_amount:,.2f} AZN — matches calculation")
				else:
					level = "Warning"
					self.add_log("Entry Preview", level,
						f"DIFFERENCE: {item.tax_type} ({item.sub_period}) | "
						f"Existing: {existing_amount:,.2f} | Calculated: {amount:,.2f} | "
						f"Diff: {diff:+,.2f} AZN | JE: {existing['je_name']}")

					if offer_correction and abs(diff) >= 0.01:
						self.append("entry_previews", {
							"tax_type": item.tax_type,
							"sub_period": item.sub_period,
							"posting_date": posting_date,
							"debit_account": debit_acc,
							"credit_account": credit_acc,
							"amount": abs(diff),
							"existing_entry": existing["je_name"],
							"existing_amount": existing_amount,
							"difference": diff,
							"remark": (f"Correction: {remark_prefix} for {item.sub_period}. "
								f"Diff from {existing['je_name']}. "
								f"Tax Period Closing Wizard {self.name}"),
							"status": "Pending",
						})
						self.add_log("Entry Preview", "Info",
							f"  → Correction entry offered: {abs(diff):,.2f} AZN")
			else:
				self.append("entry_previews", {
					"tax_type": item.tax_type,
					"sub_period": item.sub_period,
					"posting_date": posting_date,
					"debit_account": debit_acc,
					"credit_account": credit_acc,
					"amount": amount,
					"remark": f"{remark_prefix} for {item.sub_period}. "
						f"Tax Period Closing Wizard {self.name}",
					"status": "Pending",
				})

				self.add_log("Entry Preview", "Success",
					f"NEW: {item.tax_type} ({item.sub_period}) | "
					f"Dr {debit_acc} → Cr {credit_acc} | "
					f"{amount:,.2f} AZN | Date: {posting_date}")

	def _find_existing_entry(self, debit_account, credit_account, posting_date, item):
		"""Scan GL Entries to find an existing JE matching the tax type and period."""
		pd = getdate(posting_date)

		if item.period_type == "Monthly":
			period_start = date(pd.year, pd.month, 1)
			period_end = date(pd.year, pd.month, monthrange(pd.year, pd.month)[1])
		elif item.period_type == "Quarterly":
			q_start_month = ((pd.month - 1) // 3) * 3 + 1
			q_end_month = q_start_month + 2
			period_start = date(pd.year, q_start_month, 1)
			period_end = date(pd.year, q_end_month, monthrange(pd.year, q_end_month)[1])
		else:
			period_start = getdate(self.from_date)
			period_end = getdate(self.to_date)

		# Find ALL JEs that have both debit and credit lines matching our accounts
		# Sum them up — there may be multiple entries per period (e.g. from e-taxes imports)
		results = frappe.db.sql("""
			SELECT
				je.name as je_name,
				jea_d.debit_in_account_currency as amount
			FROM `tabJournal Entry` je
			INNER JOIN `tabJournal Entry Account` jea_d
				ON jea_d.parent = je.name
				AND jea_d.account = %(debit_account)s
				AND jea_d.debit_in_account_currency > 0
			INNER JOIN `tabJournal Entry Account` jea_c
				ON jea_c.parent = je.name
				AND jea_c.account = %(credit_account)s
				AND jea_c.credit_in_account_currency > 0
			WHERE je.company = %(company)s
				AND je.posting_date BETWEEN %(start)s AND %(end)s
				AND je.docstatus = 1
				AND je.is_opening = 'No'
			ORDER BY je.posting_date
		""", {
			"debit_account": debit_account,
			"credit_account": credit_account,
			"company": self.company,
			"start": str(period_start),
			"end": str(period_end),
		}, as_dict=True)

		if results:
			total = sum(flt(r.amount) for r in results)
			je_names = ", ".join(r.je_name for r in results[:3])
			if len(results) > 3:
				je_names += f" (+{len(results) - 3} more)"
			return {
				"je_name": je_names,
				"amount": total,
				"count": len(results),
			}
		return None

	def _get_accounts_for_tax(self, tax_type, settings):
		# First check account_map table
		for row in settings.account_map or []:
			if row.tax_type == tax_type:
				return {"debit": row.debit_account, "credit": row.credit_account}

		# Fallback for VAT
		if tax_type == "VAT (ƏDV)":
			return {
				"debit": settings.vat_output_account or "",
				"credit": settings.vat_input_account or "",
			}
		return None

	def _get_posting_date(self, item):
		# Posting date = last day of the sub-period
		parts = item.sub_period.split()
		if item.period_type == "Monthly" and len(parts) >= 2:
			m_name = parts[0]
			year = int(parts[1])
			m_num = MONTH_MAP.get(m_name, 1)
			return date(year, m_num, monthrange(year, m_num)[1])
		return getdate(self.to_date)

	# ── Phase 6: Create Journal Entries ───────────────────────

	def create_journal_entries(self):
		self._emit_progress(82, "Entry Creation", "Creating journal entries...")
		self.add_log("Entry Creation", "Header", "Creating journal entries...")
		settings = self._get_settings()

		# Check for duplicate closing
		if not settings.allow_reclosing:
			existing = frappe.get_all("Tax Period Closing Wizard", filters={
				"company": self.company,
				"from_date": self.from_date,
				"to_date": self.to_date,
				"wizard_status": ["in", ["Entries Created", "Completed"]],
				"name": ["!=", self.name],
				"docstatus": ["!=", 2],
			})
			if existing:
				frappe.throw(
					_("Period {0} — {1} already closed by {2}. "
					  "Enable 'Allow re-closing' in Settings to override.").format(
						self.from_date, self.to_date, existing[0].name))

		self.journal_entries = []
		total_amount = 0
		created_count = 0

		existing_count = sum(1 for p in self.entry_previews if p.status == "Existing")
		if existing_count:
			self.add_log("Entry Creation", "Info",
				f"Skipping {existing_count} entries — already exist in the system.")

		for preview in self.entry_previews:
			if preview.status not in ("Pending",):
				continue
			if flt(preview.amount) == 0:
				continue

			try:
				je = self._create_single_je(preview, settings)
				preview.status = "Created"

				self.append("journal_entries", {
					"journal_entry": je.name,
					"tax_type": preview.tax_type,
					"sub_period": preview.sub_period,
					"amount": preview.amount,
					"posting_date": preview.posting_date,
				})

				total_amount += flt(preview.amount)
				created_count += 1

				status_text = "submitted" if je.docstatus == 1 else "draft"
				self.add_log("Entry Creation", "Success",
					f"Journal Entry {je.name} created ({status_text}) | "
					f"{preview.tax_type} ({preview.sub_period}) | "
					f"{flt(preview.amount):,.2f} AZN")

			except Exception as e:
				preview.status = "Failed"
				self.add_log("Entry Creation", "Error",
					f"FAILED: {preview.tax_type} ({preview.sub_period}) — {str(e)}")
				frappe.log_error(f"JE creation error: {str(e)}")
				raise

		self.total_tax_amount = total_amount
		self.grand_total_tax_burden = total_amount + flt(self.total_payroll_taxes)

		self.add_log("Entry Creation", "Success",
			f"Created {created_count} journal entries. "
			f"Total: {total_amount:,.2f} AZN")

	def _create_single_je(self, preview, settings):
		je = frappe.new_doc("Journal Entry")
		je.company = self.company
		je.posting_date = preview.posting_date
		je.voucher_type = "Journal Entry"
		je.user_remark = preview.remark
		je.multi_currency = 0

		je.append("accounts", {
			"account": preview.debit_account,
			"debit_in_account_currency": flt(preview.amount),
			"cost_center": self.cost_center,
		})

		je.append("accounts", {
			"account": preview.credit_account,
			"credit_in_account_currency": flt(preview.amount),
			"cost_center": self.cost_center,
		})

		je.insert(ignore_permissions=True)

		if self.auto_submit or settings.auto_submit_entries:
			je.submit()

		return je

	# ── Phase 7: Period Closing Voucher ───────────────────────

	def create_period_closing_voucher(self):
		if not self.enable_period_closing_voucher:
			return

		if not self.closing_account:
			settings = self._get_settings()
			self.closing_account = settings.closing_account

		if not self.closing_account:
			self.add_log("Period Closing", "Error",
				"Closing account not specified. Cannot create Period Closing Voucher.")
			return

		self.add_log("Period Closing", "Header", "Creating Period Closing Voucher...")

		try:
			pcv = frappe.new_doc("Period Closing Voucher")
			pcv.company = self.company
			pcv.posting_date = self.to_date
			pcv.fiscal_year = self.fiscal_year
			pcv.closing_account_head = self.closing_account
			pcv.remarks = (
				f"Period Closing via Tax Period Closing Wizard {self.name}. "
				f"Period: {self.from_date} — {self.to_date}"
			)

			# Set period dates
			pcv.period_start_date = self.from_date
			pcv.period_end_date = self.to_date
			pcv.transaction_date = self.to_date

			pcv.insert(ignore_permissions=True)
			pcv.submit()

			self.add_log("Period Closing", "Success",
				f"Period Closing Voucher {pcv.name} created and submitted. "
				f"Closing Account: {self.closing_account}")

		except Exception as e:
			self.add_log("Period Closing", "Error",
				f"Failed to create Period Closing Voucher: {str(e)}")
			frappe.log_error(f"PCV creation error: {str(e)}")

	# ── Phase 8: Summary ─────────────────────────────────────

	def generate_summary(self):
		self._emit_progress(95, "Summary", "Generating summary report...")
		self.add_log("Summary", "Header", "=" * 50)
		self.add_log("Summary", "Header", "SUMMARY REPORT")
		self.add_log("Summary", "Header", "=" * 50)

		# Tax entries summary
		for item in self.tax_items:
			if item.status in ("Calculated", "Entry Created"):
				self.add_log("Summary", "Success",
					f"{item.tax_type} ({item.sub_period}): "
					f"{flt(item.tax_amount):,.2f} AZN — {item.status}")
			elif item.status == "Data Only":
				self.add_log("Summary", "Info",
					f"{item.tax_type} ({item.sub_period}): Data collected")
			elif item.status == "Skipped":
				self.add_log("Summary", "Info",
					f"{item.tax_type} ({item.sub_period}): Skipped")
			elif item.status == "Error":
				self.add_log("Summary", "Error",
					f"{item.tax_type} ({item.sub_period}): ERROR")

		self.add_log("Summary", "Header", "-" * 50)
		self.add_log("Summary", "Success",
			f"Total Tax Entries: {flt(self.total_tax_amount):,.2f} AZN")
		self.add_log("Summary", "Info",
			f"Total Payroll Taxes (collected): {flt(self.total_payroll_taxes):,.2f} AZN")
		self.add_log("Summary", "Success",
			f"Grand Total Tax Burden: {flt(self.grand_total_tax_burden):,.2f} AZN")

		# Journal entries list
		self.add_log("Summary", "Header", "-" * 50)
		self.add_log("Summary", "Header", "CREATED DOCUMENTS")
		for je_row in self.journal_entries or []:
			self.add_log("Summary", "Success",
				f"Journal Entry: {je_row.journal_entry} | "
				f"{je_row.tax_type} | {flt(je_row.amount):,.2f} AZN")

		# Deadlines
		self.add_log("Summary", "Header", "-" * 50)
		self.add_log("Summary", "Header", "DEADLINES")
		for item in self.tax_items:
			if item.enabled and item.status not in ("Skipped",):
				if item.declaration_deadline:
					self.add_log("Summary", "Warning",
						f"{item.tax_type} — Declaration: {item.declaration_deadline} | "
						f"Payment: {item.payment_deadline}")

	# ── Helpers ───────────────────────────────────────────────

	def _get_settings(self):
		return frappe.get_single("Tax Period Closing Settings")

	# ── Cancel All Entries (Rollback) ─────────────────────────

	def rollback_entries(self):
		self.add_log("Summary", "Header", "ROLLBACK — Cancelling all journal entries...")
		cancelled = []
		failed = []

		for row in self.journal_entries or []:
			if not row.journal_entry:
				continue
			try:
				je = frappe.get_doc("Journal Entry", row.journal_entry)
				if je.docstatus == 1:
					je.cancel()
					cancelled.append(row.journal_entry)
					self.add_log("Summary", "Warning",
						f"Cancelled: {row.journal_entry} | "
						f"{row.tax_type} | {flt(row.amount):,.2f} AZN")
				elif je.docstatus == 0:
					je.delete()
					cancelled.append(row.journal_entry)
					self.add_log("Summary", "Warning",
						f"Deleted (draft): {row.journal_entry}")
			except Exception as e:
				failed.append(row.journal_entry)
				self.add_log("Summary", "Error",
					f"Failed to cancel {row.journal_entry}: {str(e)}")

		self.journal_entries = []
		self.wizard_status = "Draft"
		self.total_tax_amount = 0
		self.grand_total_tax_burden = flt(self.total_payroll_taxes)

		self.add_log("Summary", "Success" if not failed else "Warning",
			f"Rollback complete. Cancelled: {len(cancelled)} | Failed: {len(failed)}")

		self.save()


# ── Whitelisted API Methods ──────────────────────────────────

@frappe.whitelist()
def run_wizard(name):
	doc = frappe.get_doc("Tax Period Closing Wizard", name)
	doc.check_permission("write")

	doc.log_entries = []
	doc.progress_percent = 0
	doc.wizard_status = "Draft"

	# Phase 1
	doc.progress_percent = 5
	doc.detect_company_info()

	# Phase 1.5
	doc.detect_benefits()

	# Phase 2
	doc.progress_percent = 15
	doc.detect_taxes()

	# Phase 3
	doc.progress_percent = 30
	doc.collect_financial_data()

	# Phase 4
	doc.progress_percent = 50
	doc.calculate_taxes()

	doc.wizard_status = "Calculated"

	# Phase 5
	doc.generate_previews()

	doc._emit_progress(80, "Complete", "Calculation complete. Review results below.", "Success")
	doc.progress_percent = 80

	doc.save()
	frappe.db.commit()

	return {"status": "calculated", "name": doc.name}


@frappe.whitelist()
def create_entries(name):
	doc = frappe.get_doc("Tax Period Closing Wizard", name)
	doc.check_permission("write")

	# Phase 6
	doc.create_journal_entries()
	doc.progress_percent = 90

	# Phase 7
	doc.create_period_closing_voucher()
	doc.progress_percent = 95

	# Phase 8
	doc.generate_summary()

	# Update tax item statuses
	for item in doc.tax_items:
		if item.status == "Calculated":
			item.status = "Entry Created"

	doc.wizard_status = "Entries Created"
	doc.progress_percent = 100

	doc._emit_progress(100, "Complete", "All journal entries created successfully!", "Success")

	doc.save()
	frappe.db.commit()

	return {"status": "entries_created", "name": doc.name}


@frappe.whitelist()
def rollback(name):
	doc = frappe.get_doc("Tax Period Closing Wizard", name)
	doc.check_permission("write")
	doc.rollback_entries()
	return {"status": "rolled_back", "name": doc.name}


@frappe.whitelist()
def verify_entries(name):
	"""Verify all journal entries for cash method compliance."""
	doc = frappe.get_doc("Tax Period Closing Wizard", name)
	doc.check_permission("read")

	company = doc.company
	accounting_method = frappe.db.get_value("Company", company, "accounting_method")
	settings = frappe.get_single("Tax Period Closing Settings")

	results = {
		"method": accounting_method or _("Accrual method"),
		"issues": [],
		"ok": [],
		"summary": {},
	}

	if accounting_method != "Kassa metodu":
		results["ok"].append(_("Company uses accrual method — no cash method verification needed."))
		return results

	vat_output = settings.vat_output_account
	deferred_vat_sales = settings.deferred_vat_sales_account

	# Find all Sales Invoices in the period
	invoices = frappe.get_all("Sales Invoice", filters={
		"company": company,
		"posting_date": ["between", [str(doc.from_date), str(doc.to_date)]],
		"docstatus": 1,
	}, fields=["name", "grand_total", "net_total", "total_taxes_and_charges",
		"outstanding_amount", "status"])

	total_invoices = len(invoices)
	paid_ok = 0
	unpaid_wrong = 0
	partial_issues = 0
	total_wrong_vat = 0

	for si in invoices:
		vat_amount = flt(si.total_taxes_and_charges)
		if vat_amount == 0:
			continue

		is_paid = flt(si.outstanding_amount) == 0
		is_partial = 0 < flt(si.outstanding_amount) < flt(si.grand_total)
		is_unpaid = flt(si.outstanding_amount) == flt(si.grand_total)

		# Check: did VAT go to 521.3 directly (wrong for kassa) or 245.1 (correct)?
		gl_vat_direct = frappe.db.sql("""
			SELECT SUM(credit) as total
			FROM `tabGL Entry`
			WHERE voucher_type = 'Sales Invoice'
			AND voucher_no = %s
			AND account = %s
			AND is_cancelled = 0
		""", (si.name, vat_output), as_dict=True)

		vat_on_521 = flt(gl_vat_direct[0].total) if gl_vat_direct and gl_vat_direct[0].total else 0

		gl_vat_deferred = frappe.db.sql("""
			SELECT SUM(credit) as total
			FROM `tabGL Entry`
			WHERE voucher_type = 'Sales Invoice'
			AND voucher_no = %s
			AND account = %s
			AND is_cancelled = 0
		""", (si.name, deferred_vat_sales), as_dict=True)

		vat_on_245 = flt(gl_vat_deferred[0].total) if gl_vat_deferred and gl_vat_deferred[0].total else 0

		if vat_on_521 > 0 and is_unpaid:
			# WRONG: VAT on 521.3 but invoice not paid
			unpaid_wrong += 1
			total_wrong_vat += vat_on_521
			results["issues"].append({
				"type": "error",
				"invoice": si.name,
				"message": (f"VAT {vat_on_521:,.2f} AZN posted to 521.3 (liability), "
					f"but invoice is unpaid. Under cash method, VAT should only be "
					f"recognized when payment is received."),
				"amount": vat_on_521,
				"fix": f"Move {vat_on_521:,.2f} from 521.3 to 245.1",
			})
		elif vat_on_521 > 0 and is_partial:
			# PARTIAL: some VAT on 521.3 but invoice partially paid
			paid_ratio = 1 - (flt(si.outstanding_amount) / flt(si.grand_total))
			expected_vat = flt(vat_amount * paid_ratio, 2)
			excess = flt(vat_on_521 - expected_vat, 2)
			if excess > 0.01:
				partial_issues += 1
				total_wrong_vat += excess
				results["issues"].append({
					"type": "warning",
					"invoice": si.name,
					"message": (f"VAT {vat_on_521:,.2f} AZN on 521.3, but only "
						f"{paid_ratio*100:.0f}% paid. Expected VAT: {expected_vat:,.2f}. "
						f"Excess: {excess:,.2f} AZN."),
					"amount": excess,
					"fix": f"Move {excess:,.2f} from 521.3 to 245.1",
				})
			else:
				paid_ok += 1
				results["ok"].append(f"{si.name}: VAT correct ({paid_ratio*100:.0f}% paid)")
		elif vat_on_521 > 0 and is_paid:
			# OK: VAT on 521.3 and invoice is paid
			paid_ok += 1
			results["ok"].append(f"{si.name}: VAT correct (fully paid, {vat_on_521:,.2f} AZN)")
		elif vat_on_245 > 0:
			# CORRECT: VAT on 245.1 (deferred)
			paid_ok += 1
			results["ok"].append(f"{si.name}: VAT correct on 245.1 (deferred, {vat_on_245:,.2f} AZN)")

	results["summary"] = {
		"total_invoices": total_invoices,
		"correct": paid_ok,
		"errors": unpaid_wrong,
		"warnings": partial_issues,
		"total_wrong_vat": total_wrong_vat,
	}

	return results


@frappe.whitelist()
def fix_cash_method_entries(name):
	"""Fix Sales Invoices IN-PLACE for cash method.
	Directly updates GL Entry accounts and Invoice tax/item accounts.
	No new documents created. Same invoices, correct GL."""
	doc = frappe.get_doc("Tax Period Closing Wizard", name)
	doc.check_permission("write")

	company = doc.company
	accounting_method = frappe.db.get_value("Company", company, "accounting_method")

	if accounting_method != "Kassa metodu":
		return {"status": "skip", "message": _("Company does not use cash method.")}

	settings = frappe.get_single("Tax Period Closing Settings")
	vat_output = settings.vat_output_account
	deferred_vat_sales = settings.deferred_vat_sales_account
	deferred_revenue = settings.deferred_revenue_account

	if not deferred_vat_sales or not vat_output:
		return {"status": "error", "message": _("Deferred VAT accounts not configured in Settings.")}

	invoices = frappe.get_all("Sales Invoice", filters={
		"company": company,
		"posting_date": ["between", [str(doc.from_date), str(doc.to_date)]],
		"docstatus": 1,
	}, fields=["name", "grand_total", "net_total", "total_taxes_and_charges",
		"outstanding_amount", "posting_date"])

	fixed = []
	skipped = []
	errors = []

	for si_data in invoices:
		vat_amount = flt(si_data.total_taxes_and_charges)
		if vat_amount == 0:
			skipped.append({"invoice": si_data.name, "reason": _("No VAT")})
			continue

		# Check if already fixed
		vat_on_245 = flt(frappe.db.sql("""
			SELECT SUM(credit) FROM `tabGL Entry`
			WHERE voucher_type='Sales Invoice' AND voucher_no=%s
			AND account=%s AND is_cancelled=0
		""", (si_data.name, deferred_vat_sales))[0][0] or 0)

		if vat_on_245 > 0:
			skipped.append({"invoice": si_data.name, "reason": _("Already fixed")})
			continue

		try:
			changes = []

			# 1. Fix GL Entry: 521.3 → 245.1 (VAT)
			vat_updated = frappe.db.sql("""
				UPDATE `tabGL Entry`
				SET account = %s
				WHERE voucher_type = 'Sales Invoice' AND voucher_no = %s
				AND account = %s AND is_cancelled = 0
			""", (deferred_vat_sales, si_data.name, vat_output))
			vat_rows = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
			if vat_rows:
				changes.append(f"GL: {vat_output} → {deferred_vat_sales} ({vat_rows} rows)")

			# 2. Fix GL Entry: 601 → 542 (Revenue)
			rev_rows = 0
			if deferred_revenue:
				# Find which income account this invoice uses
				income_accounts = frappe.db.sql("""
					SELECT DISTINCT account FROM `tabGL Entry`
					WHERE voucher_type = 'Sales Invoice' AND voucher_no = %s
					AND is_cancelled = 0 AND credit > 0
					AND account NOT LIKE '521%%' AND account NOT LIKE '245%%'
				""", si_data.name, as_dict=True)

				for acc in income_accounts:
					frappe.db.sql("""
						UPDATE `tabGL Entry`
						SET account = %s
						WHERE voucher_type = 'Sales Invoice' AND voucher_no = %s
						AND account = %s AND is_cancelled = 0
					""", (deferred_revenue, si_data.name, acc.account))
					rows = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
					if rows:
						rev_rows += rows
						changes.append(f"GL: {acc.account} → {deferred_revenue} ({rows} rows)")

			# 3. Update Invoice tax account_head (so re-opening shows correct account)
			frappe.db.sql("""
				UPDATE `tabSales Taxes and Charges`
				SET account_head = %s
				WHERE parent = %s AND account_head = %s
			""", (deferred_vat_sales, si_data.name, vat_output))

			# 4. Update Invoice item income_account
			if deferred_revenue:
				frappe.db.sql("""
					UPDATE `tabSales Invoice Item`
					SET income_account = %s
					WHERE parent = %s AND income_account != %s
				""", (deferred_revenue, si_data.name, deferred_revenue))

			fixed.append({
				"invoice": si_data.name,
				"vat": vat_amount,
				"revenue": flt(si_data.net_total),
				"changes": changes,
			})

		except Exception as e:
			errors.append({"invoice": si_data.name, "error": str(e)})

	frappe.db.commit()

	return {
		"status": "done",
		"fixed": fixed,
		"skipped": skipped,
		"errors": errors,
		"summary": {
			"invoices_fixed": len(fixed),
			"total_vat_moved": sum(f["vat"] for f in fixed),
			"total_revenue_deferred": sum(f["revenue"] for f in fixed),
			"skipped": len(skipped),
			"errors": len(errors),
		}
	}
