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

from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
from datetime import date, timedelta


class PayrollEntry(models.TransientModel):
    """
    Payroll Entry - Represents a single employee's payroll for a period.
    This is the QuickBooks-like payroll line item.
    Transient because it's linked to the wizard.
    """
    _name = 'payroll.entry'
    _description = 'Payroll Entry'
    _order = 'employee_id'

    wizard_id = fields.Many2one(
        'run.payroll.wizard',
        string='Payroll Wizard',
        ondelete='cascade',
    )
    
    employee_id = fields.Many2one(
        'hr.employee',
        string='Employee',
        required=True,
        ondelete='cascade',
    )
    
    # Display fields for Edit Paycheque dialog
    employee_address = fields.Char(
        string='Employee Address',
        compute='_compute_display_fields',
    )
    pay_date = fields.Date(
        string='Pay Date',
        related='wizard_id.pay_date',
    )
    pay_period_display = fields.Char(
        string='Pay Period',
        compute='_compute_display_fields',
    )
    paid_from = fields.Char(
        string='Paid From',
        compute='_compute_display_fields',
    )
    
    # Employee info (computed for display)
    employee_type = fields.Selection([
        ('hourly', 'Hourly'),
        ('salary', 'Salary'),
    ], string='Type', compute='_compute_employee_info')
    
    hourly_rate = fields.Monetary(
        string='Hourly Rate',
        currency_field='currency_id',
        compute='_compute_employee_info',
    )
    
    # Pay components
    regular_hours = fields.Float(
        string='Regular Hours',
        default=0.0,
    )
    regular_pay = fields.Monetary(
        string='Regular Pay',
        currency_field='currency_id',
        compute='_compute_pay_amounts',
    )
    
    vacation_pay_percent = fields.Float(
        string='Vacation %',
        default=4.0,
        help='Vacation pay percentage (default 4%)',
    )
    vacation_pay = fields.Monetary(
        string='Vacation Pay',
        currency_field='currency_id',
        compute='_compute_pay_amounts',
    )
    
    stat_holiday_hours = fields.Float(
        string='Stat Holiday Hours',
        default=0.0,
    )
    stat_holiday_pay = fields.Monetary(
        string='Stat Holiday Pay',
        currency_field='currency_id',
        compute='_compute_pay_amounts',
    )
    
    stat_pay_avg_daily_wage = fields.Monetary(
        string='Stat Pay - Avg Daily Wage',
        currency_field='currency_id',
        default=0.0,
        help='Additional stat pay based on average daily wage',
    )
    
    # Totals
    total_hours = fields.Float(
        string='Total Hrs',
        compute='_compute_totals',
    )
    gross_pay = fields.Monetary(
        string='Gross Pay',
        currency_field='currency_id',
        compute='_compute_totals',
    )
    
    # Deductions (Employee Taxes)
    income_tax = fields.Monetary(
        string='Income Tax',
        currency_field='currency_id',
        compute='_compute_taxes',
    )
    employment_insurance = fields.Monetary(
        string='Employment Insurance',
        currency_field='currency_id',
        compute='_compute_taxes',
    )
    cpp = fields.Monetary(
        string='Canada Pension Plan',
        currency_field='currency_id',
        compute='_compute_taxes',
    )
    cpp2 = fields.Monetary(
        string='Second Canada Pension Plan',
        currency_field='currency_id',
        compute='_compute_taxes',
    )
    total_employee_tax = fields.Monetary(
        string='Total Employee Tax',
        currency_field='currency_id',
        compute='_compute_taxes',
    )
    
    # Employer Taxes
    employer_ei = fields.Monetary(
        string='EI Employer',
        currency_field='currency_id',
        compute='_compute_employer_taxes',
    )
    employer_cpp = fields.Monetary(
        string='CPP Employer',
        currency_field='currency_id',
        compute='_compute_employer_taxes',
    )
    employer_cpp2 = fields.Monetary(
        string='CPP2 Employer',
        currency_field='currency_id',
        compute='_compute_employer_taxes',
    )
    total_employer_tax = fields.Monetary(
        string='Total Employer Tax',
        currency_field='currency_id',
        compute='_compute_employer_taxes',
    )
    
    # Net Pay
    net_pay = fields.Monetary(
        string='Net Pay',
        currency_field='currency_id',
        compute='_compute_net_pay',
    )
    
    # Vacation Time Off Tracking
    vacation_hours_accrued = fields.Float(
        string='Vacation Hours Accrued',
        default=0.0,
    )
    vacation_hours_used = fields.Float(
        string='Vacation Hours Used',
        default=0.0,
    )
    vacation_hours_available = fields.Float(
        string='Vacation Hours Available',
        compute='_compute_vacation_balance',
    )
    vacation_amount_accrued = fields.Monetary(
        string='Vacation Amount Accrued',
        currency_field='currency_id',
        compute='_compute_vacation_balance',
    )
    vacation_amount_used = fields.Monetary(
        string='Vacation Amount Used',
        currency_field='currency_id',
        default=0.0,
    )
    vacation_amount_available = fields.Monetary(
        string='Vacation Amount Available',
        currency_field='currency_id',
        compute='_compute_vacation_balance',
    )
    
    # Other fields
    memo = fields.Text(
        string='Memo',
    )
    payment_method = fields.Selection([
        ('cheque', 'Paper cheque'),
        ('direct_deposit', 'Direct Deposit'),
    ], string='Pay Method', default='cheque')
    
    # Currency
    currency_id = fields.Many2one(
        'res.currency',
        string='Currency',
        default=lambda self: self.env.company.currency_id,
    )
    
    # Previous payroll indicator
    previous_payroll_id = fields.Many2one(
        'hr.payslip',
        string='Previous Payroll',
        compute='_compute_previous_payroll',
    )
    previous_payroll_amount = fields.Monetary(
        string='Previous Amount',
        currency_field='currency_id',
        compute='_compute_previous_payroll',
    )
    previous_payroll_date = fields.Date(
        string='Previous Pay Date',
        compute='_compute_previous_payroll',
    )
    has_previous_payroll = fields.Boolean(
        string='Has Previous',
        compute='_compute_previous_payroll',
    )
    change_from_last_payroll = fields.Char(
        string='Change from Last',
        compute='_compute_previous_payroll',
        help='Percentage change from last payroll',
    )

    @api.depends('employee_id')
    def _compute_employee_info(self):
        for entry in self:
            if entry.employee_id:
                employee = entry.employee_id
                
                # Get pay type and rate from Fusion Payroll fields
                pay_type = getattr(employee, 'pay_type', 'hourly')
                
                if pay_type == 'hourly':
                    entry.employee_type = 'hourly'
                    # Use hourly_rate from Fusion Payroll
                    hourly_rate = getattr(employee, 'hourly_rate', 0.0)
                    entry.hourly_rate = hourly_rate if hourly_rate else 0.0
                elif pay_type == 'salary':
                    entry.employee_type = 'salary'
                    # Calculate hourly from salary
                    salary = getattr(employee, 'salary_amount', 0.0)
                    hours_per_week = getattr(employee, 'default_hours_per_week', 40.0) or 40.0
                    # Bi-weekly = salary / 2, then divide by hours per pay period
                    entry.hourly_rate = (salary / 2) / hours_per_week if salary else 0.0
                else:
                    entry.employee_type = 'hourly'
                    entry.hourly_rate = 0.0
                
                # If still no rate, fallback to contract or hourly_cost
                if entry.hourly_rate == 0.0:
                    if hasattr(employee, 'hourly_cost') and employee.hourly_cost:
                        entry.hourly_rate = employee.hourly_cost
                    elif hasattr(employee, 'contract_id') and employee.contract_id:
                        contract = employee.contract_id
                        if hasattr(contract, 'wage') and contract.wage:
                            entry.hourly_rate = contract.wage / 160
            else:
                entry.employee_type = 'hourly'
                entry.hourly_rate = 0.0

    @api.depends('regular_hours', 'hourly_rate', 'vacation_pay_percent', 'stat_holiday_hours')
    def _compute_pay_amounts(self):
        for entry in self:
            # Regular pay = hours * rate
            entry.regular_pay = entry.regular_hours * entry.hourly_rate
            
            # Vacation pay = vacation_pay_percent% of regular pay
            entry.vacation_pay = entry.regular_pay * (entry.vacation_pay_percent / 100)
            
            # Stat holiday pay = hours * rate
            entry.stat_holiday_pay = entry.stat_holiday_hours * entry.hourly_rate

    @api.depends('regular_hours', 'stat_holiday_hours', 'regular_pay', 'vacation_pay', 'stat_holiday_pay', 'stat_pay_avg_daily_wage')
    def _compute_totals(self):
        for entry in self:
            entry.total_hours = entry.regular_hours + entry.stat_holiday_hours
            entry.gross_pay = entry.regular_pay + entry.vacation_pay + entry.stat_holiday_pay + entry.stat_pay_avg_daily_wage

    @api.depends('gross_pay', 'employee_id')
    def _compute_taxes(self):
        """Calculate employee tax deductions."""
        for entry in self:
            if entry.gross_pay <= 0:
                entry.income_tax = 0
                entry.employment_insurance = 0
                entry.cpp = 0
                entry.cpp2 = 0
                entry.total_employee_tax = 0
                continue
            
            # Get tax rates from parameters or use defaults
            # These are simplified calculations - actual payroll uses full tax rules
            gross = entry.gross_pay
            
            # Simplified tax calculations (bi-weekly)
            # Income tax: ~15-20% average for Canadian employees
            entry.income_tax = round(gross * 0.128, 2)  # Approximate federal + provincial
            
            # EI: 1.64% of gross (2025 rate) up to maximum
            entry.employment_insurance = round(min(gross * 0.0164, 1049.12 / 26), 2)
            
            # CPP: 5.95% of pensionable earnings above basic exemption (2025)
            cpp_exempt = 3500 / 26  # Annual exemption / 26 pay periods
            pensionable = max(0, gross - cpp_exempt)
            entry.cpp = round(min(pensionable * 0.0595, 4034.10 / 26), 2)
            
            # CPP2: 4% on earnings above first ceiling (2025)
            entry.cpp2 = 0  # Only applies if earnings exceed $71,300/year
            
            entry.total_employee_tax = entry.income_tax + entry.employment_insurance + entry.cpp + entry.cpp2

    @api.depends('employment_insurance', 'cpp', 'cpp2')
    def _compute_employer_taxes(self):
        """Calculate employer tax contributions."""
        for entry in self:
            # EI employer: 1.4x employee rate
            entry.employer_ei = round(entry.employment_insurance * 1.4, 2)
            
            # CPP employer: same as employee
            entry.employer_cpp = entry.cpp
            
            # CPP2 employer: same as employee
            entry.employer_cpp2 = entry.cpp2
            
            entry.total_employer_tax = entry.employer_ei + entry.employer_cpp + entry.employer_cpp2

    @api.depends('gross_pay', 'total_employee_tax')
    def _compute_net_pay(self):
        for entry in self:
            entry.net_pay = entry.gross_pay - entry.total_employee_tax

    @api.depends('vacation_pay', 'vacation_hours_used')
    def _compute_vacation_balance(self):
        for entry in self:
            entry.vacation_hours_available = entry.vacation_hours_accrued - entry.vacation_hours_used
            entry.vacation_amount_accrued = entry.vacation_pay  # Current period accrual
            entry.vacation_amount_available = entry.vacation_amount_accrued - entry.vacation_amount_used

    @api.depends('employee_id', 'wizard_id.date_start', 'wizard_id.date_end', 'gross_pay')
    def _compute_previous_payroll(self):
        """Check if employee has been paid in the current period and compute change."""
        for entry in self:
            entry.previous_payroll_id = False
            entry.previous_payroll_amount = 0
            entry.previous_payroll_date = False
            entry.has_previous_payroll = False
            entry.change_from_last_payroll = ''
            
            if not entry.employee_id:
                continue
            
            # Search for the last payslip for this employee (not in current period)
            payslip = self.env['hr.payslip'].search([
                ('employee_id', '=', entry.employee_id.id),
                ('state', 'in', ['done', 'paid']),
            ], limit=1, order='date_to desc')
            
            if payslip:
                entry.previous_payroll_id = payslip
                entry.previous_payroll_amount = payslip.net_wage
                entry.previous_payroll_date = payslip.date_to
                entry.has_previous_payroll = True
                
                # Calculate change percentage
                if payslip.basic_wage and payslip.basic_wage > 0 and entry.gross_pay > 0:
                    change = ((entry.gross_pay - payslip.basic_wage) / payslip.basic_wage) * 100
                    if change > 0:
                        entry.change_from_last_payroll = f"↑ Up {abs(change):.0f}%"
                    elif change < 0:
                        entry.change_from_last_payroll = f"↓ Down {abs(change):.0f}%"
                    else:
                        entry.change_from_last_payroll = "No change"

    def action_load_attendance_hours(self):
        """Load hours from hr_attendance for the pay period."""
        self.ensure_one()
        
        if not self.wizard_id:
            return
        
        # Search for attendance records in the period
        attendances = self.env['hr.attendance'].search([
            ('employee_id', '=', self.employee_id.id),
            ('check_in', '>=', self.wizard_id.date_start),
            ('check_in', '<=', self.wizard_id.date_end),
            ('check_out', '!=', False),
        ])
        
        total_hours = 0
        for att in attendances:
            if att.check_out:
                delta = att.check_out - att.check_in
                total_hours += delta.total_seconds() / 3600
        
        self.regular_hours = round(total_hours, 2)
        
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Attendance Hours Loaded'),
                'message': _('Loaded %.2f hours from attendance records.') % total_hours,
                'type': 'success',
            },
        }

    def action_view_previous_payroll(self):
        """Open the previous payroll record."""
        self.ensure_one()
        if self.previous_payroll_id:
            return {
                'type': 'ir.actions.act_window',
                'name': _('Previous Payslip'),
                'res_model': 'hr.payslip',
                'res_id': self.previous_payroll_id.id,
                'view_mode': 'form',
                'target': 'new',
            }

    def action_open_edit_paycheque(self):
        """Open the detailed paycheque editor dialog."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': _('Edit Paycheque - %s') % self.employee_id.name,
            'res_model': 'payroll.entry',
            'res_id': self.id,
            'view_mode': 'form',
            'target': 'new',
            'context': {'form_view_ref': 'fusion_payroll.payroll_entry_edit_form'},
        }

    @api.depends('employee_id', 'wizard_id')
    def _compute_display_fields(self):
        for entry in self:
            # Employee address
            if entry.employee_id:
                emp = entry.employee_id
                parts = []
                if hasattr(emp, 'home_street') and emp.home_street:
                    parts.append(emp.home_street)
                if hasattr(emp, 'home_city') and emp.home_city:
                    city_part = emp.home_city
                    if hasattr(emp, 'home_province') and emp.home_province:
                        city_part += f", {emp.home_province}"
                    if hasattr(emp, 'home_postal_code') and emp.home_postal_code:
                        city_part += f" {emp.home_postal_code}"
                    parts.append(city_part)
                entry.employee_address = '\n'.join(parts) if parts else ''
            else:
                entry.employee_address = ''
            
            # Pay period display
            if entry.wizard_id and entry.wizard_id.date_start and entry.wizard_id.date_end:
                entry.pay_period_display = f"{entry.wizard_id.date_start.strftime('%m.%d.%Y')} to {entry.wizard_id.date_end.strftime('%m.%d.%Y')}"
            else:
                entry.pay_period_display = ''
            
            # Paid from (company bank or cheque info)
            if entry.wizard_id and entry.wizard_id.company_id:
                company = entry.wizard_id.company_id
                if entry.payment_method == 'cheque':
                    entry.paid_from = f"Cheque ({company.name})"
                else:
                    entry.paid_from = f"Direct Deposit ({company.name})"
            else:
                entry.paid_from = ''

    def action_save_entry(self):
        """Save the entry and close the dialog."""
        self.ensure_one()
        return {'type': 'ir.actions.act_window_close'}
