from odoo import api, SUPERUSER_ID


def post_init_hook(env_or_cr, registry=None):
    if registry is not None:
        env = api.Environment(env_or_cr, SUPERUSER_ID, {})
    else:
        env = env_or_cr
    PayrollSetup(env).run()


class PayrollSetup:
    def __init__(self, env):
        self.env = env
        self.Category = env["hr.salary.rule.category"]
        self.Register = env["hr.contribution.register"]
        self.Rule = env["hr.salary.rule"]
        self.RuleInput = env["hr.rule.input"]
        self.Structure = env["hr.payroll.structure"]
        self.Company = env["res.company"]

    def run(self):
        categories = self._ensure_categories()
        for company in self.Company.search([("country_id.code", "=", "IN")]):
            self._ensure_india(company, categories)
        for company in self.Company.search([("country_id.code", "=", "CA")]):
            self._ensure_canada(company, categories)

    def _ensure_categories(self):
        specs = [
            ("BASIC", "Basic"),
            ("ALW", "Allowance"),
            ("GROSS", "Gross"),
            ("DED", "Deduction"),
            ("NET", "Net"),
            ("COMP", "Company Contribution"),
        ]
        result = {}
        for code, name in specs:
            category = self.Category.search([("code", "=", code)], limit=1)
            if not category:
                category = self.Category.create({"code": code, "name": name})
            result[code] = category
        return result

    def _ensure_register(self, name):
        register = self.Register.search([("name", "=", name)], limit=1)
        if not register:
            register = self.Register.create({"name": name})
        return register

    def _ensure_base_structure(self, company, categories):
        base = self.Structure.search([("code", "=", "BASE"), ("company_id", "=", company.id)], limit=1)
        values = {
            "name": "Base Payroll Structure",
            "code": "BASE",
            "company_id": company.id,
            "rule_ids": [(6, 0, [])],
        }
        if not base:
            base = self.Structure.create(values)
        else:
            base.write(values)
        return base

    def _rule(self, company, name, code, sequence, category, python, register=False):
        domain = [("code", "=", code)]
        domain.append(("company_id", "=", company.id if company else False))
        rule = self.Rule.search(domain, limit=1)
        values = {
            "name": name,
            "code": code,
            "sequence": sequence,
            "category_id": category.id,
            "company_id": company.id if company else False,
            "condition_select": "none",
            "amount_select": "code",
            "amount_python_compute": python.strip(),
            "appears_on_payslip": True,
        }
        if register:
            values["register_id"] = register.id
        if rule:
            rule.write(values)
        else:
            rule = self.Rule.create(values)
        return rule

    def _input(self, rule, code, name):
        item = self.RuleInput.search([("input_id", "=", rule.id), ("code", "=", code)], limit=1)
        if not item:
            item = self.RuleInput.create({"input_id": rule.id, "code": code, "name": name})
        return item

    def _structure(self, company, code, name, rules):
        base = self._ensure_base_structure(company, self._ensure_categories())
        structure = self.Structure.search([("code", "=", code), ("company_id", "=", company.id)], limit=1)
        values = {
            "name": name,
            "code": code,
            "company_id": company.id,
            "parent_id": base.id,
            "rule_ids": [(6, 0, [rule.id for rule in rules])],
        }
        if structure:
            structure.write(values)
        else:
            structure = self.Structure.create(values)
        return structure

    def _ensure_india(self, company, categories):
        pf = self._ensure_register("India Employees Provident Fund")
        esi = self._ensure_register("India Employees State Insurance")
        tax = self._ensure_register("India Payroll Tax Deductions")
        rules = [
            self._rule(company, "Basic Salary", "BASIC", 10, categories["BASIC"], """
result = contract.wage * 0.50
"""),
            self._rule(company, "House Rent Allowance", "HRA", 20, categories["ALW"], """
result = contract.wage * 0.20
"""),
            self._rule(company, "Special Allowance", "ALLOW", 30, categories["ALW"], """
result = contract.wage * 0.30
"""),
            self._rule(company, "Gross", "GROSS", 100, categories["GROSS"], """
result = categories.BASIC + categories.ALW
"""),
            self._rule(company, "Employee Provident Fund", "EPF", 150, categories["DED"], """
base = categories.BASIC
if base > 15000.0:
    base = 15000.0
result = -(base * 0.12)
""", pf),
            self._rule(company, "Employee State Insurance", "ESI", 160, categories["DED"], """
result = 0.0
if categories.GROSS <= 21000.0:
    result = -(categories.GROSS * 0.0075)
""", esi),
            self._rule(company, "Professional Tax", "IN_PT", 170, categories["DED"], """
result = -(inputs.IN_PT.amount) if inputs.IN_PT else 0.0
""", tax),
            self._rule(company, "Income Tax TDS", "IN_TDS", 180, categories["DED"], """
result = -(inputs.IN_TDS.amount) if inputs.IN_TDS else 0.0
""", tax),
            self._rule(company, "Net Salary", "NET", 300, categories["NET"], """
result = categories.BASIC + categories.ALW + categories.DED
"""),
        ]
        self._input(rules[6], "IN_PT", "Professional Tax")
        self._input(rules[7], "IN_TDS", "Income Tax TDS")
        self._structure(company, "MCS_IN_PAYROLL", "India Monthly Payroll", rules)

    def _ensure_canada(self, company, categories):
        cpp = self._ensure_register("Canada Pension Plan")
        ei = self._ensure_register("Employment Insurance")
        tax = self._ensure_register("Canada Payroll Tax Deductions")
        rules = [
            self._rule(company, "Regular Salary", "BASIC", 10, categories["BASIC"], """
result = contract.wage
"""),
            self._rule(company, "Gross", "GROSS", 100, categories["GROSS"], """
result = categories.BASIC + categories.ALW
"""),
            self._rule(company, "CPP Employee Contribution", "CPP", 150, categories["DED"], """
monthly_exemption = 3500.0 / 12.0
monthly_max = 74600.0 / 12.0
base = categories.GROSS
if base > monthly_max:
    base = monthly_max
base = base - monthly_exemption
if base < 0.0:
    base = 0.0
result = -(base * 0.0595)
""", cpp),
            self._rule(company, "CPP2 Employee Contribution", "CPP2", 155, categories["DED"], """
lower = 74600.0 / 12.0
upper = 85000.0 / 12.0
base = categories.GROSS
if base < lower:
    base = lower
if base > upper:
    base = upper
base = base - lower
result = -(base * 0.04)
""", cpp),
            self._rule(company, "Employment Insurance", "EI", 160, categories["DED"], """
monthly_max = 68900.0 / 12.0
base = categories.GROSS
if base > monthly_max:
    base = monthly_max
result = -(base * 0.0163)
""", ei),
            self._rule(company, "Federal and Ontario Income Tax", "CA_TAX", 180, categories["DED"], """
result = -(inputs.CA_TAX.amount) if inputs.CA_TAX else 0.0
""", tax),
            self._rule(company, "Net Salary", "NET", 300, categories["NET"], """
result = categories.BASIC + categories.ALW + categories.DED
"""),
        ]
        self._input(rules[5], "CA_TAX", "Federal and Ontario Income Tax")
        self._structure(company, "MCS_CA_PAYROLL", "Canada Monthly Payroll", rules)
