# -*- coding: utf-8 -*-

from odoo import api, fields, models, _
from odoo.exceptions import UserError


class HrPayslip(models.Model):
    _inherit = 'hr.payslip'

    # === Additional Fields for QuickBooks-style Paycheque Entry ===
    cheque_id = fields.Many2one(
        'payroll.cheque',
        string='Cheque',
        copy=False,
        help='Linked cheque record for paper cheque payments',
    )
    cheque_number = fields.Char(
        string='Cheque Number',
        related='cheque_id.cheque_number',
        store=True,
        copy=False,
        help='Cheque number for paper cheque payments',
    )
    cheque_state = fields.Selection(
        related='cheque_id.state',
        string='Cheque Status',
        store=True,
    )
    memo = fields.Text(
        string='Memo',
        help='Internal notes for this paycheque',
    )
    paid_by = fields.Selection([
        ('cheque', 'Paper Cheque'),
        ('direct_deposit', 'Direct Deposit'),
    ], string='Payment Method', compute='_compute_paid_by', store=True, readonly=False)
    
    @api.depends('employee_id', 'employee_id.payment_method')
    def _compute_paid_by(self):
        """Set payment method from employee's default payment method."""
        for payslip in self:
            if payslip.employee_id and hasattr(payslip.employee_id, 'payment_method'):
                payslip.paid_by = payslip.employee_id.payment_method or 'direct_deposit'
            else:
                payslip.paid_by = 'direct_deposit'
    
    def action_print_cheque(self):
        """Print cheque for this payslip - always opens wizard to set/change cheque number."""
        self.ensure_one()
        
        if self.paid_by != 'cheque':
            raise UserError(_("This payslip is not set to be paid by cheque."))
        
        # Create cheque if not exists
        if not self.cheque_id:
            cheque = self.env['payroll.cheque'].create_from_payslip(self)
            if cheque:
                self.cheque_id = cheque.id
            else:
                raise UserError(_("Failed to create cheque. Check employee payment method."))
        
        # Always open the cheque number wizard to allow changing the number
        return {
            'type': 'ir.actions.act_window',
            'name': _('Set Cheque Number'),
            'res_model': 'payroll.cheque.number.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': {
                'default_cheque_id': self.cheque_id.id,
            },
        }
    
    def action_create_cheque(self):
        """Create a cheque for this payslip without printing."""
        self.ensure_one()
        
        if self.paid_by != 'cheque':
            raise UserError(_("This payslip is not set to be paid by cheque."))
        
        if self.cheque_id:
            raise UserError(_("A cheque already exists for this payslip."))
        
        cheque = self.env['payroll.cheque'].create_from_payslip(self)
        if cheque:
            self.cheque_id = cheque.id
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'title': _('Cheque Created'),
                    'message': _('Cheque created for %s.') % self.employee_id.name,
                    'type': 'success',
                    'sticky': False,
                }
            }
        else:
            raise UserError(_("Failed to create cheque."))

    # === YTD Computed Fields ===
    ytd_gross = fields.Monetary(
        string='YTD Gross',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date gross earnings',
    )
    ytd_cpp = fields.Monetary(
        string='YTD CPP',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date CPP contributions (employee)',
    )
    ytd_cpp2 = fields.Monetary(
        string='YTD CPP2',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date CPP2 contributions (employee)',
    )
    ytd_ei = fields.Monetary(
        string='YTD EI',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date EI contributions (employee)',
    )
    ytd_income_tax = fields.Monetary(
        string='YTD Income Tax',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date income tax withheld',
    )
    ytd_net = fields.Monetary(
        string='YTD Net',
        compute='_compute_ytd_amounts',
        currency_field='currency_id',
        help='Year-to-date net pay',
    )

    # === Employer Tax Totals (for display) ===
    employer_cpp = fields.Monetary(
        string='Employer CPP',
        compute='_compute_employer_contributions',
        currency_field='currency_id',
    )
    employer_cpp2 = fields.Monetary(
        string='Employer CPP2',
        compute='_compute_employer_contributions',
        currency_field='currency_id',
    )
    employer_ei = fields.Monetary(
        string='Employer EI',
        compute='_compute_employer_contributions',
        currency_field='currency_id',
    )
    total_employer_cost = fields.Monetary(
        string='Total Employer Cost',
        compute='_compute_employer_contributions',
        currency_field='currency_id',
        help='Total employer contributions (CPP + CPP2 + EI)',
    )

    # === Employee Tax Totals (for summary) ===
    employee_cpp = fields.Monetary(
        string='Employee CPP',
        compute='_compute_employee_deductions',
        currency_field='currency_id',
    )
    employee_cpp2 = fields.Monetary(
        string='Employee CPP2',
        compute='_compute_employee_deductions',
        currency_field='currency_id',
    )
    employee_ei = fields.Monetary(
        string='Employee EI',
        compute='_compute_employee_deductions',
        currency_field='currency_id',
    )
    employee_income_tax = fields.Monetary(
        string='Income Tax',
        compute='_compute_employee_deductions',
        currency_field='currency_id',
    )
    total_employee_deductions = fields.Monetary(
        string='Total Employee Deductions',
        compute='_compute_employee_deductions',
        currency_field='currency_id',
    )

    @api.depends('employee_id', 'date_from', 'line_ids', 'line_ids.total')
    def _compute_ytd_amounts(self):
        """Calculate year-to-date amounts for each payslip"""
        for payslip in self:
            if not payslip.employee_id or not payslip.date_from:
                payslip.ytd_gross = 0
                payslip.ytd_cpp = 0
                payslip.ytd_cpp2 = 0
                payslip.ytd_ei = 0
                payslip.ytd_income_tax = 0
                payslip.ytd_net = 0
                continue

            # Get the start of the year
            year_start = payslip.date_from.replace(month=1, day=1)
            
            # Find all payslips for this employee in the same year, up to and including this one
            domain = [
                ('employee_id', '=', payslip.employee_id.id),
                ('date_from', '>=', year_start),
                ('date_to', '<=', payslip.date_to),
                ('state', 'in', ['done', 'paid']),
            ]
            # Include current payslip if it's in draft/verify state
            if payslip.state in ['draft', 'verify']:
                domain = ['|', ('id', '=', payslip.id)] + domain

            ytd_payslips = self.search(domain)

            # Calculate YTD totals
            ytd_gross = 0
            ytd_cpp = 0
            ytd_cpp2 = 0
            ytd_ei = 0
            ytd_income_tax = 0
            ytd_net = 0

            for slip in ytd_payslips:
                ytd_gross += slip.gross_wage or 0
                ytd_net += slip.net_wage or 0
                
                # Sum up specific rule amounts
                for line in slip.line_ids:
                    code = line.code or ''
                    if code == 'CPP':
                        ytd_cpp += abs(line.total or 0)
                    elif code == 'CPP2':
                        ytd_cpp2 += abs(line.total or 0)
                    elif code == 'EI':
                        ytd_ei += abs(line.total or 0)
                    elif code in ['FED_TAX', 'PROV_TAX', 'INCOME_TAX']:
                        ytd_income_tax += abs(line.total or 0)

            payslip.ytd_gross = ytd_gross
            payslip.ytd_cpp = ytd_cpp
            payslip.ytd_cpp2 = ytd_cpp2
            payslip.ytd_ei = ytd_ei
            payslip.ytd_income_tax = ytd_income_tax
            payslip.ytd_net = ytd_net

    @api.depends('line_ids', 'line_ids.total', 'line_ids.code')
    def _compute_employer_contributions(self):
        """Calculate employer contribution totals from payslip lines"""
        for payslip in self:
            employer_cpp = 0
            employer_cpp2 = 0
            employer_ei = 0

            for line in payslip.line_ids:
                code = line.code or ''
                if code == 'CPP_ER':
                    employer_cpp = abs(line.total or 0)
                elif code == 'CPP2_ER':
                    employer_cpp2 = abs(line.total or 0)
                elif code == 'EI_ER':
                    employer_ei = abs(line.total or 0)

            payslip.employer_cpp = employer_cpp
            payslip.employer_cpp2 = employer_cpp2
            payslip.employer_ei = employer_ei
            payslip.total_employer_cost = employer_cpp + employer_cpp2 + employer_ei

    @api.depends('line_ids', 'line_ids.total', 'line_ids.code')
    def _compute_employee_deductions(self):
        """Calculate employee deduction totals from payslip lines"""
        for payslip in self:
            employee_cpp = 0
            employee_cpp2 = 0
            employee_ei = 0
            employee_income_tax = 0

            for line in payslip.line_ids:
                code = line.code or ''
                if code == 'CPP':
                    employee_cpp = abs(line.total or 0)
                elif code == 'CPP2':
                    employee_cpp2 = abs(line.total or 0)
                elif code == 'EI':
                    employee_ei = abs(line.total or 0)
                elif code in ['FED_TAX', 'PROV_TAX', 'INCOME_TAX']:
                    employee_income_tax += abs(line.total or 0)

            payslip.employee_cpp = employee_cpp
            payslip.employee_cpp2 = employee_cpp2
            payslip.employee_ei = employee_ei
            payslip.employee_income_tax = employee_income_tax
            payslip.total_employee_deductions = (
                employee_cpp + employee_cpp2 + employee_ei + employee_income_tax
            )

    # =========================================================================
    # PAY TYPE IDENTIFICATION HELPERS (for T4 and ROE reporting)
    # =========================================================================

    @api.model
    def _get_pay_type_from_code(self, code, category_code=None):
        """
        Map salary rule code to pay type for ROE/T4 reporting.
        
        Returns one of: 'salary', 'hourly', 'overtime', 'bonus', 'stat_holiday',
        'commission', 'allowance', 'reimbursement', 'union_dues', 'other'
        
        :param code: Salary rule code (e.g., 'OT_PAY', 'BONUS_PAY')
        :param category_code: Category code (e.g., 'BASIC', 'ALW', 'DED')
        :return: Pay type string
        """
        if not code:
            return 'other'
        
        code_upper = code.upper()
        
        # Direct code matches
        if code_upper == 'OT_PAY':
            return 'overtime'
        elif code_upper == 'BONUS_PAY' or code_upper == 'BONUS':
            return 'bonus'
        elif code_upper == 'STAT_PAY' or code_upper == 'STAT_HOLIDAY':
            return 'stat_holiday'
        
        # Pattern matching for commissions
        if 'COMMISSION' in code_upper or 'COMM' in code_upper:
            return 'commission'
        
        # Pattern matching for union dues
        if 'UNION' in code_upper or 'DUES' in code_upper:
            return 'union_dues'
        
        # Pattern matching for reimbursements
        if 'REIMBURSEMENT' in code_upper or 'REIMB' in code_upper:
            return 'reimbursement'
        
        # Pattern matching for allowances
        if 'ALLOWANCE' in code_upper or 'ALW' in code_upper:
            # Check if it's a reimbursement first
            if 'REIMBURSEMENT' in code_upper or 'REIMB' in code_upper:
                return 'reimbursement'
            return 'allowance'
        
        # Category-based identification
        if category_code:
            category_upper = category_code.upper()
            if category_upper == 'BASIC':
                return 'salary'  # Could be salary or hourly, default to salary
            elif category_upper == 'ALW':
                # Already checked for allowance patterns above
                return 'allowance'
            elif category_upper == 'DED':
                # Deductions - check if union dues
                if 'UNION' in code_upper or 'DUES' in code_upper:
                    return 'union_dues'
                return 'other'
        
        return 'other'

    @api.model
    def _is_reimbursement(self, code, category_code=None):
        """
        Check if salary rule code represents a reimbursement (non-taxable).
        
        :param code: Salary rule code
        :param category_code: Category code
        :return: True if reimbursement, False otherwise
        """
        if not code:
            return False
        
        code_upper = code.upper()
        
        # Direct pattern matching
        if 'REIMBURSEMENT' in code_upper or 'REIMB' in code_upper:
            return True
        
        return False
