# Copyright (c) 2025, Jey Soft and contributors
# For license information, please see license.txt

import calendar

import frappe
from frappe.model.document import Document
from frappe.utils import flt, getdate

from taxes_az.taxes_az.report.employee_payroll_register.employee_payroll_register import (
	DEDUCTION_TYPES,
	EARNING_TYPES,
	get_employee_doj_map,
	get_salary_slip_details,
	get_salary_slips,
)

QUARTER_DATE_RANGES = {
	"1. Rüb": ("01-01", "03-31"),
	"2. Rüb": ("04-01", "06-30"),
	"3. Rüb": ("07-01", "09-30"),
	"4. Rüb": ("10-01", "12-31"),
}

# Scrubbed keys for insurance formula calculation
_DSMF_ISCI = frappe.scrub("DSMF (işçi)")
_DSMF_SIRKET = frappe.scrub("DSMF (şirkət)")
_ISSIZLIK_ISCI = frappe.scrub("İşsizlikdən sığorta haqqı (işçi 0,5%)")
_ISSIZLIK_SIRKET = frappe.scrub("İşsizlikdən sığorta haqqı (şirkət 0,5%)")
_ITS_ISCI = frappe.scrub("İcbari tibbi sığorta (işçi 2%)")
_ITS_SIRKET = frappe.scrub("İcbari tibbi sığorta (şirkət 2%)")
_KOMPENSASIYA = frappe.scrub("Məzuniyyət Kompensasiyası")

# Maps Salary Slip tax-free indicator/amount fields to elave4 child table rows.
ELAVE4_SPECS = [
	{
		"key": "vergi_data",
		"indicator_field": "custom_vergi_indicator",
		"amount_field": "custom_vergi_amount",
		"row_amount_field": "gəlirvergisindənazadolanməbləğmanatla",
	},
	{
		"key": "mdss_data",
		"indicator_field": "custom_mdss_indicator",
		"amount_field": "custom_mdss_amount",
		"row_amount_field": "cəlbolunmayangəlirlərmanatla",
	},
	{
		"key": "issizlik_data",
		"indicator_field": "custom_unemployment_insurance_indicator",
		"amount_field": "custom_unemployment_insurance_amount",
		"row_amount_field": "cəlbolunmayangəlirlərmanatla",
	},
	{
		"key": "its_data",
		"indicator_field": "custom_medical_insurance_indicator",
		"amount_field": "custom_medical_insurance_amount",
		"row_amount_field": "cəlbolunmayangəlirlərmanatla",
	},
]


class Singledeclarationrelatedtosalariedandnonsalariedwork(Document):
	pass


@frappe.whitelist()
def get_company_defaults(company):
	"""Fetch Company fields with Unicode names that can't go through client-side get_value."""
	doc = frappe.get_doc("Company", company)
	return {
		"mdssüzrə": doc.get("mdssüzrə") or "",
		"icbaritibbisığortaüzrə": doc.get("icbaritibbisığortaüzrə") or "",
		"işsizlikdənsığortaüzrə": doc.get("işsizlikdənsığortaüzrə") or "",
		"main_activity": doc.get("main_activity") or "",
	}


@frappe.whitelist()
def populate_declaration_tables(company, year, quarter):
	"""Populate child tables from payroll and absence data.

	Returns dict with data for each child table.
	"""
	try:
		year = int(year)
		if quarter not in QUARTER_DATE_RANGES:
			return {"success": False, "error": f"Invalid quarter: {quarter}"}

		start_suffix, end_suffix = QUARTER_DATE_RANGES[quarter]
		from_date = f"{year}-{start_suffix}"
		to_date = f"{year}-{end_suffix}"

		# Fetch payroll data once, build hissə_1 and hissə_2 from it
		payroll = fetch_payroll_data(company, from_date, to_date)
		emp_monthly_taxable = _per_employee_monthly_taxable(payroll, quarter)
		hisse_1_data = build_hisse_1(payroll, emp_monthly_taxable)
		hisse_2_data = build_hisse_2(payroll)
		hisse_3_data = get_absence_data(company, from_date, to_date)
		bolme2_hisse1_data = get_bolme2_hisse1_data(company, year, quarter)
		bolme_3_data = build_bolme_3(emp_monthly_taxable)
		elave4_data = build_elave4_tables(payroll)

		return {
			"success": True,
			"hisse_1_data": hisse_1_data,
			"hisse_2_data": hisse_2_data,
			"hisse_3_data": hisse_3_data,
			"bolme2_hisse1_data": bolme2_hisse1_data,
			"bolme_3_data": bolme_3_data,
			**elave4_data,
		}

	except Exception as e:
		frappe.log_error(
			f"Error in populate_declaration_tables: {str(e)}",
			"Single Declaration",
		)
		return {"success": False, "error": str(e)}


def fetch_payroll_data(company, from_date, to_date):
	"""Fetch and aggregate salary slip data per employee. Shared by hissə_1 and hissə_2."""
	import erpnext

	company_currency = erpnext.get_company_currency(company)
	currency = "AZN"

	filters = {
		"company": company,
		"from_date": from_date,
		"to_date": to_date,
		"currency": currency,
		"docstatus": "Submitted",
	}

	salary_slips = get_salary_slips(filters, company_currency)
	if not salary_slips:
		return {
			"employee_map": {},
			"fin_map": {},
			"doj_map": {},
			"salary_slips": [],
			"currency": currency,
			"company_currency": company_currency,
		}

	ss_earning_map = get_salary_slip_details(salary_slips, currency, company_currency, "earnings")
	ss_ded_map = get_salary_slip_details(salary_slips, currency, company_currency, "deductions")
	doj_map = get_employee_doj_map()

	all_scrubbed_keys = [frappe.scrub(e) for e in EARNING_TYPES] + [frappe.scrub(d) for d in DEDUCTION_TYPES]
	sum_fields = ["gross_pay", "total_deduction", "net_pay"] + all_scrubbed_keys

	employee_map = {}

	for ss in salary_slips:
		emp = ss.employee

		if emp not in employee_map:
			employee_map[emp] = {
				"employee": emp,
				"employee_name": ss.employee_name,
			}
			for field in sum_fields:
				employee_map[emp][field] = 0.0

		agg = employee_map[emp]

		for e in EARNING_TYPES:
			agg[frappe.scrub(e)] += flt(ss_earning_map.get(ss.name, {}).get(e, 0))

		if currency == company_currency:
			agg["gross_pay"] += flt(ss.gross_pay) * flt(ss.exchange_rate)
		else:
			agg["gross_pay"] += flt(ss.gross_pay)

		for d in DEDUCTION_TYPES:
			agg[frappe.scrub(d)] += flt(ss_ded_map.get(ss.name, {}).get(d, 0))

		if currency == company_currency:
			agg["total_deduction"] += flt(ss.total_deduction) * flt(ss.exchange_rate)
			agg["net_pay"] += flt(ss.net_pay) * flt(ss.exchange_rate)
		else:
			agg["total_deduction"] += flt(ss.total_deduction)
			agg["net_pay"] += flt(ss.net_pay)

	# Get FIN (passport_number) from Employee doctype
	employee_ids = list(employee_map.keys())
	fin_map = {}
	if employee_ids:
		fin_data = frappe.get_all("Employee", filters={"name": ["in", employee_ids]}, fields=["name", "passport_number"])
		fin_map = {d.name: d.passport_number for d in fin_data}

	return {
		"employee_map": employee_map,
		"fin_map": fin_map,
		"doj_map": doj_map,
		"salary_slips": salary_slips,
		"currency": currency,
		"company_currency": company_currency,
	}


def build_hisse_1(payroll, emp_monthly_taxable):
	"""Build hissə_1 rows from aggregated payroll data."""
	employee_map = payroll["employee_map"]
	fin_map = payroll["fin_map"]

	emp_quarterly_tax = {}
	for (emp, _mi), taxable in emp_monthly_taxable.items():
		emp_quarterly_tax[emp] = emp_quarterly_tax.get(emp, 0.0) + _calc_income_tax(taxable)

	result = []
	for agg in employee_map.values():
		emp = agg["employee"]
		child_row = {
			"soyadadataadı": agg["employee_name"],
			"fin": fin_map.get(emp, ""),
			"gəlirlərcəmi": agg.get(frappe.scrub("Əsas əmək haqqı"), 0),
			"vergiməbləği": round(emp_quarterly_tax.get(emp, 0.0), 2),
			"hesablanmışmdsshaqqı": flt(agg.get(_DSMF_ISCI, 0)) + flt(agg.get(_DSMF_SIRKET, 0)),
			"hesablanmışişsizlikdənsığortahaqqı": flt(agg.get(_ISSIZLIK_ISCI, 0)) + flt(agg.get(_ISSIZLIK_SIRKET, 0)),
			"hesablanmışicbaritibbisığortahaqqı": flt(agg.get(_ITS_ISCI, 0)) + flt(agg.get(_ITS_SIRKET, 0)),
		}
		result.append(child_row)

	result.sort(key=lambda x: x.get("soyadadataadı", ""))
	return result


def build_hisse_2(payroll):
	"""Build hissə_2 rows from aggregated payroll data. Only employees with compensation."""
	employee_map = payroll["employee_map"]
	fin_map = payroll["fin_map"]
	doj_map = payroll["doj_map"]

	result = []
	for agg in employee_map.values():
		compensation = flt(agg.get(_KOMPENSASIYA, 0))
		if not compensation:
			continue

		emp = agg["employee"]
		doj = doj_map.get(emp)
		doj_year = str(getdate(doj).year) if doj else ""

		child_row = {
			"işçininsığortaolunanınsoyadıadıatasınınadı": agg["employee_name"],
			"fin": fin_map.get(emp, ""),
			"il": doj_year,
			"kompensasiyaməbləği": compensation,
		}
		result.append(child_row)

	result.sort(key=lambda x: x.get("işçininsığortaolunanınsoyadıadıatasınınadı", ""))
	return result


def build_elave4_tables(payroll):
	"""Group salary slips by tax-free indicator into elave4 child tables.

	One row per (table, indicator): count of distinct employees and the summed
	exempt amount over the quarter. Slips without an indicator are skipped.
	"""
	salary_slips = payroll["salary_slips"]
	currency = payroll["currency"]
	company_currency = payroll["company_currency"]

	output = {}
	for spec in ELAVE4_SPECS:
		groups = {}
		for ss in salary_slips:
			indicator = ss.get(spec["indicator_field"])
			if not indicator:
				continue
			amount = flt(ss.get(spec["amount_field"]))
			if currency == company_currency:
				amount *= flt(ss.exchange_rate)
			entry = groups.setdefault(indicator, {"employees": set(), "amount": 0.0})
			entry["employees"].add(ss.employee)
			entry["amount"] += amount

		rows = []
		for indicator, agg in groups.items():
			rows.append({
				"göstəricilər": indicator,
				"işçilərinsayınəfərlə": len(agg["employees"]),
				spec["row_amount_field"]: agg["amount"],
			})
		rows.sort(key=lambda x: x.get("göstəricilər", ""))
		output[spec["key"]] = rows
	return output


def _calc_income_tax(gross):
	# AZ progressive brackets: 3% / 75 + 10% over 2500 / 625 + 14% over 8000.
	# Brackets are per-employee per-month — sum of per-employee taxes ≠ tax on summed base.
	if gross <= 0:
		return 0.0
	if gross <= 2500:
		return gross * 0.03
	if gross <= 8000:
		return 75 + (gross - 2500) * 0.10
	return 625 + (gross - 8000) * 0.14


def _per_employee_monthly_taxable(payroll, quarter):
	"""Group salary slips into {(employee, month_index_1to3): taxable_income_sum}.

	Taxable = gross_pay − custom_vergi_amount per slip. Slips fully marked as
	income-tax-exempt (custom_vergi_indicator set, amount == gross_pay) drop to 0
	and don't contribute to the bracket.
	"""
	salary_slips = payroll["salary_slips"]
	currency = payroll["currency"]
	company_currency = payroll["company_currency"]

	quarter_num = int(quarter[0])
	start_month = (quarter_num - 1) * 3 + 1

	result = {}
	for ss in salary_slips:
		month_index = ss.start_date.month - start_month + 1
		if month_index not in (1, 2, 3):
			continue
		rate = flt(ss.exchange_rate) if currency == company_currency else 1.0
		gp = flt(ss.gross_pay) * rate
		exempt = flt(ss.get("custom_vergi_amount") or 0) * rate
		taxable = max(gp - exempt, 0.0)
		key = (ss.employee, month_index)
		result[key] = result.get(key, 0.0) + taxable
	return result


def build_bolme_3(emp_monthly_taxable):
	"""Income tax (gəlir vergisi) per month, computed per-employee then summed."""
	monthly_tax = {1: 0.0, 2: 0.0, 3: 0.0}
	for (_emp, mi), taxable in emp_monthly_taxable.items():
		monthly_tax[mi] += _calc_income_tax(taxable)

	return {
		"Muzdlu işlə əlaqədar hesablamalar": {
			"vergiməbləği": round(monthly_tax[1], 2),
			"vergiməbləği2": round(monthly_tax[2], 2),
			"vergiməbləği3": round(monthly_tax[3], 2),
			"vergiməbləği4": round(monthly_tax[1] + monthly_tax[2] + monthly_tax[3], 2),
		}
	}


def get_bolme2_hisse1_data(company, year, quarter):
	"""Build data for bolme2_hisse1: employee counts per month of quarter."""
	quarter_num = int(quarter[0])  # "1. Rüb" -> 1
	months = [(quarter_num - 1) * 3 + i for i in range(1, 4)]  # e.g. Q1 -> [1, 2, 3]

	total_counts = []
	present_counts = []

	for month in months:
		last_day = calendar.monthrange(year, month)[1]
		month_start = f"{year}-{month:02d}-01"
		month_end = f"{year}-{month:02d}-{last_day:02d}"

		# Row 1: total employees on payroll
		total = frappe.db.sql("""
			SELECT COUNT(*) FROM `tabEmployee`
			WHERE company = %(company)s
			AND status != 'Inactive'
			AND date_of_joining <= %(month_end)s
			AND (
				(relieving_date >= %(month_start)s)
				OR (relieving_date IS NULL AND status != 'Left')
			)
		""", {"company": company, "month_start": month_start, "month_end": month_end})[0][0]
		total_counts.append(total)

		# Row 2: unique employees who actually showed up
		present = frappe.db.sql("""
			SELECT COUNT(DISTINCT a.employee) FROM `tabAttendance` a
			WHERE a.company = %(company)s
			AND a.docstatus = 1
			AND a.status IN ('Present', 'Half Day')
			AND a.attendance_date >= %(month_start)s
			AND a.attendance_date <= %(month_end)s
		""", {"company": company, "month_start": month_start, "month_end": month_end})[0][0]
		present_counts.append(present)

	total_avg = round(sum(total_counts) / 3) if total_counts else 0
	present_avg = round(sum(present_counts) / 3) if present_counts else 0

	return {
		"İşçilərin ümumi sayı": {
			"1ciay": total_counts[0] if len(total_counts) > 0 else 0,
			"2ciay": total_counts[1] if len(total_counts) > 1 else 0,
			"3cüay": total_counts[2] if len(total_counts) > 2 else 0,
			"rübüzrəortasay": total_avg,
		},
		"Faktiki işə cəlb olunan işçilərin sayı": {
			"1ciay": present_counts[0] if len(present_counts) > 0 else 0,
			"2ciay": present_counts[1] if len(present_counts) > 1 else 0,
			"3cüay": present_counts[2] if len(present_counts) > 2 else 0,
			"rübüzrəortasay": present_avg,
		},
	}


def get_absence_data(company, from_date, to_date):
	"""Fetch absence data from Monthly Absence Report for hissə_3."""
	from taxes_az.taxes_az.report.monthly_absence_report.monthly_absence_report import get_data

	filters = {
		"company": company,
		"from_date": from_date,
		"to_date": to_date,
	}

	data = get_data(filters)
	if not data:
		return []

	result = []
	for row in data:
		name_parts = [row.get("last_name", ""), row.get("first_name", "")]
		full_name = " ".join(p for p in name_parts if p)

		child_row = {
			"soyadadataadı": full_name,
			"fin": row.get("fin", ""),
			"işçininişləmədiyiişgünlərininsəbəbi": row.get("absent_reason", ""),
			"işçininişləmədiyiişgünlərininsayı": row.get("days_absent", 0),
		}
		result.append(child_row)

	return result
