# -*- coding: utf-8 -*-
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError


class Payslip(models.Model):
    _name = 'c2c.payslip'
    _description = 'Employee Payslip'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = 'date_from desc, id desc'
    _rec_name = 'display_name'

    # ------------------------------------------------------------------
    # Core fields
    # ------------------------------------------------------------------
    employee_id = fields.Many2one(
        'hr.employee', string='Employee', required=True,
        tracking=True,
    )
    contract_id = fields.Many2one(
        'hr.contract', string='Contract', required=True,
        tracking=True,
        domain="[('employee_id', '=', employee_id), ('state', '=', 'open')]",
    )
    date_from = fields.Date(
        string='Period From', required=True,
    )
    date_to = fields.Date(
        string='Period To', required=True,
    )
    company_id = fields.Many2one(
        'res.company', string='Company', required=True,
        default=lambda self: self.env.company,
    )

    # ------------------------------------------------------------------
    # Earnings (computed & stored)
    # ------------------------------------------------------------------
    gross_salary = fields.Float(
        string='Gross Salary', compute='_compute_salary', store=True,
    )
    basic = fields.Float(
        string='Basic', compute='_compute_salary', store=True,
    )
    hra = fields.Float(
        string='HRA', compute='_compute_salary', store=True,
    )
    allowances = fields.Float(
        string='Allowances', compute='_compute_salary', store=True,
    )

    # ------------------------------------------------------------------
    # Deductions (computed & stored)
    # ------------------------------------------------------------------
    pf_deduction = fields.Float(
        string='PF Deduction', compute='_compute_salary', store=True,
    )
    esi_deduction = fields.Float(
        string='ESI Deduction', compute='_compute_salary', store=True,
    )
    professional_tax = fields.Float(
        string='Professional Tax', compute='_compute_salary', store=True,
    )
    total_deductions = fields.Float(
        string='Total Deductions', compute='_compute_salary', store=True,
    )

    # ------------------------------------------------------------------
    # Net
    # ------------------------------------------------------------------
    net_salary = fields.Float(
        string='Net Salary', compute='_compute_salary', store=True,
    )

    # ------------------------------------------------------------------
    # State & Accounting
    # ------------------------------------------------------------------
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirmed', 'Confirmed'),
        ('paid', 'Paid'),
    ], string='Status', default='draft', tracking=True, copy=False)

    journal_entry_id = fields.Many2one(
        'account.move', string='Journal Entry', readonly=True, copy=False,
    )
    journal_entry_count = fields.Integer(
        compute='_compute_journal_entry_count',
    )

    # ------------------------------------------------------------------
    # Display name
    # ------------------------------------------------------------------
    display_name = fields.Char(compute='_compute_display_name', store=True)

    @api.depends('employee_id', 'date_from', 'date_to')
    def _compute_display_name(self):
        for rec in self:
            emp_name = rec.employee_id.name or 'New'
            date_from = rec.date_from or ''
            date_to = rec.date_to or ''
            rec.display_name = '%s (%s - %s)' % (emp_name, date_from, date_to)

    # ------------------------------------------------------------------
    # Computed salary
    # ------------------------------------------------------------------
    @api.depends(
        'contract_id',
        'contract_id.gross_salary',
        'contract_id.salary_structure_id',
        'contract_id.salary_structure_id.basic_percentage',
        'contract_id.salary_structure_id.hra_percentage',
        'contract_id.salary_structure_id.allowance_percentage',
        'contract_id.salary_structure_id.pf_percentage',
        'contract_id.salary_structure_id.esi_percentage',
        'contract_id.salary_structure_id.professional_tax_fixed',
        'contract_id.pf_applicable',
        'contract_id.esi_applicable',
    )
    def _compute_salary(self):
        for rec in self:
            contract = rec.contract_id
            structure = contract.salary_structure_id if contract else False

            if not contract or not structure:
                rec.gross_salary = 0.0
                rec.basic = 0.0
                rec.hra = 0.0
                rec.allowances = 0.0
                rec.pf_deduction = 0.0
                rec.esi_deduction = 0.0
                rec.professional_tax = 0.0
                rec.total_deductions = 0.0
                rec.net_salary = 0.0
                continue

            gross = contract.gross_salary
            rec.gross_salary = gross

            # Earnings
            rec.basic = gross * structure.basic_percentage / 100.0
            rec.hra = gross * structure.hra_percentage / 100.0
            rec.allowances = gross * structure.allowance_percentage / 100.0

            # Deductions
            pf = (rec.basic * structure.pf_percentage / 100.0) if contract.pf_applicable else 0.0
            esi = (gross * structure.esi_percentage / 100.0) if contract.esi_applicable else 0.0
            pt = structure.professional_tax_fixed

            rec.pf_deduction = pf
            rec.esi_deduction = esi
            rec.professional_tax = pt
            rec.total_deductions = pf + esi + pt
            rec.net_salary = gross - rec.total_deductions

    # ------------------------------------------------------------------
    # Journal entry count
    # ------------------------------------------------------------------
    @api.depends('journal_entry_id')
    def _compute_journal_entry_count(self):
        for rec in self:
            rec.journal_entry_count = 1 if rec.journal_entry_id else 0

    # ------------------------------------------------------------------
    # Onchange helpers
    # ------------------------------------------------------------------
    @api.onchange('employee_id')
    def _onchange_employee_id(self):
        """Auto-fill contract when employee changes."""
        if self.employee_id:
            contract = self.env['hr.contract'].search([
                ('employee_id', '=', self.employee_id.id),
                ('state', '=', 'open'),
                ('company_id', '=', self.env.company.id),
            ], limit=1)
            self.contract_id = contract
        else:
            self.contract_id = False

    # ------------------------------------------------------------------
    # Constraints
    # ------------------------------------------------------------------
    @api.constrains('date_from', 'date_to')
    def _check_dates(self):
        for rec in self:
            if rec.date_from and rec.date_to and rec.date_from > rec.date_to:
                raise ValidationError('Period From cannot be after Period To.')

    # ------------------------------------------------------------------
    # Actions
    # ------------------------------------------------------------------
    def action_confirm(self):
        """Confirm the payslip and create an accounting journal entry."""
        for rec in self:
            if rec.state != 'draft':
                raise UserError('Only draft payslips can be confirmed.')
            if not rec.contract_id or not rec.contract_id.salary_structure_id:
                raise UserError(
                    'Please set a salary structure on the contract before confirming.'
                )
            if rec.net_salary <= 0:
                raise UserError('Net salary must be greater than zero to confirm.')

            # Get accounts from system parameters (company-specific)
            ICP = self.env['ir.config_parameter'].sudo()
            expense_account_id = int(
                ICP.get_param('c2c_payroll.salary_expense_account_id', default=0)
            )
            payable_account_id = int(
                ICP.get_param('c2c_payroll.salary_payable_account_id', default=0)
            )

            if not expense_account_id or not payable_account_id:
                raise UserError(
                    'Please configure Salary Expense and Payable accounts in '
                    'Settings → Technical → Parameters → System Parameters.\n\n'
                    'Keys:\n'
                    ' • c2c_payroll.salary_expense_account_id\n'
                    ' • c2c_payroll.salary_payable_account_id'
                )

            # Validate accounts exist
            expense_account = self.env['account.account'].browse(expense_account_id)
            payable_account = self.env['account.account'].browse(payable_account_id)
            if not expense_account.exists() or not payable_account.exists():
                raise UserError(
                    'Configured salary accounts do not exist. '
                    'Please verify the system parameter values.'
                )

            journal = self.env['account.journal'].search([
                ('type', '=', 'general'),
                ('company_id', '=', rec.company_id.id),
            ], limit=1)
            if not journal:
                raise UserError(
                    'No general journal found for company %s.' % rec.company_id.name
                )

            move_vals = {
                'journal_id': journal.id,
                'date': rec.date_to,
                'ref': 'Payslip: %s' % rec.display_name,
                'company_id': rec.company_id.id,
                'line_ids': [
                    (0, 0, {
                        'name': 'Salary Expense - %s' % rec.employee_id.name,
                        'account_id': expense_account_id,
                        'debit': rec.net_salary,
                        'credit': 0.0,
                    }),
                    (0, 0, {
                        'name': 'Employee Payable - %s' % rec.employee_id.name,
                        'account_id': payable_account_id,
                        'debit': 0.0,
                        'credit': rec.net_salary,
                    }),
                ],
            }
            move = self.env['account.move'].create(move_vals)
            rec.write({
                'state': 'confirmed',
                'journal_entry_id': move.id,
            })

    def action_mark_paid(self):
        """Mark confirming payslip as paid."""
        for rec in self:
            if rec.state != 'confirmed':
                raise UserError('Only confirmed payslips can be marked as paid.')
            rec.write({'state': 'paid'})

    def action_reset_to_draft(self):
        """Reset to draft (deletes journal entry if unposted)."""
        for rec in self:
            if rec.state == 'paid':
                raise UserError('Paid payslips cannot be reset to draft.')
            if rec.journal_entry_id:
                if rec.journal_entry_id.state == 'posted':
                    raise UserError(
                        'Cannot reset: the journal entry is already posted. '
                        'Please cancel it first.'
                    )
                rec.journal_entry_id.unlink()
            rec.write({'state': 'draft', 'journal_entry_id': False})

    def action_open_journal_entry(self):
        """Smart button to open the linked journal entry."""
        self.ensure_one()
        if not self.journal_entry_id:
            raise UserError('No journal entry linked to this payslip.')
        return {
            'type': 'ir.actions.act_window',
            'name': 'Journal Entry',
            'res_model': 'account.move',
            'res_id': self.journal_entry_id.id,
            'view_mode': 'form',
            'target': 'current',
        }
