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

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


class PayPeriod(models.Model):
    """
    Pay Period Management
    Stores configured pay periods with auto-generation of future periods.
    """
    _name = 'payroll.pay.period'
    _description = 'Pay Period'
    _order = 'date_start asc'  # Chronological: oldest first (scroll UP for past, scroll DOWN for future)
    _rec_name = 'name'

    display_order = fields.Integer(
        string='Display Order',
        compute='_compute_display_order',
        store=True,
        help='For reference only - actual ordering uses date_start desc',
    )
    
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        required=True,
        default=lambda self: self.env.company,
        ondelete='cascade',
    )
    
    date_start = fields.Date(
        string='Period Start',
        required=True,
    )
    date_end = fields.Date(
        string='Period End',
        required=True,
    )
    pay_date = fields.Date(
        string='Pay Date',
        help='Date when employees receive payment',
    )
    
    schedule_type = fields.Selection([
        ('weekly', 'Weekly'),
        ('biweekly', 'Bi-Weekly'),
        ('semi_monthly', 'Semi-Monthly'),
        ('monthly', 'Monthly'),
    ], string='Schedule Type', required=True, default='biweekly')
    
    state = fields.Selection([
        ('draft', 'Open'),
        ('in_progress', 'In Progress'),
        ('paid', 'Paid'),
        ('closed', 'Closed'),
    ], string='Status', default='draft', tracking=True)
    
    payslip_run_id = fields.Many2one(
        'hr.payslip.run',
        string='Payslip Batch',
        ondelete='set null',
    )
    
    name = fields.Char(
        string='Period Name',
        compute='_compute_name',
        store=True,
    )
    
    is_current = fields.Boolean(
        string='Current Period',
        compute='_compute_is_current',
    )

    @api.depends('date_start', 'date_end')
    def _compute_name(self):
        for period in self:
            if period.date_start and period.date_end:
                period.name = f"{period.date_start.strftime('%m.%d.%Y')} to {period.date_end.strftime('%m.%d.%Y')}"
            else:
                period.name = _('New Period')

    def _compute_is_current(self):
        today = fields.Date.context_today(self)
        for period in self:
            period.is_current = period.date_start <= today <= period.date_end

    @api.depends('date_start', 'date_end')
    def _compute_display_order(self):
        """
        Compute display order for proper dropdown sorting:
        - Current period: 0
        - Future periods: 1-999 (by days from today)
        - Past periods: 1000+ (by days from today, reversed)
        """
        today = fields.Date.context_today(self)
        for period in self:
            if not period.date_start or not period.date_end:
                period.display_order = 9999
            elif period.date_start <= today <= period.date_end:
                # Current period - top priority
                period.display_order = 0
            elif period.date_start > today:
                # Future period - closer dates have lower order
                days_ahead = (period.date_start - today).days
                period.display_order = min(days_ahead, 999)
            else:
                # Past period - more recent dates have lower order
                days_ago = (today - period.date_end).days
                period.display_order = 1000 + days_ago

    @api.constrains('date_start', 'date_end')
    def _check_dates(self):
        for period in self:
            if period.date_end < period.date_start:
                raise ValidationError(_('Period end date must be after start date.'))
            # Check for overlaps within same company and schedule type
            overlapping = self.search([
                ('id', '!=', period.id),
                ('company_id', '=', period.company_id.id),
                ('schedule_type', '=', period.schedule_type),
                ('date_start', '<=', period.date_end),
                ('date_end', '>=', period.date_start),
            ])
            if overlapping:
                raise ValidationError(_('Pay periods cannot overlap. Found overlap with: %s') % overlapping[0].name)

    @api.model
    def generate_periods(self, company_id, schedule_type, start_date, num_periods=12, pay_day_offset=7):
        """
        Generate pay periods based on schedule type.
        
        Args:
            company_id: Company ID
            schedule_type: weekly, biweekly, semi_monthly, monthly
            start_date: First period start date
            num_periods: Number of periods to generate
            pay_day_offset: Days after period end to set pay date
        """
        periods = []
        current_start = start_date
        
        for _ in range(num_periods):
            if schedule_type == 'weekly':
                current_end = current_start + timedelta(days=6)
                next_start = current_start + timedelta(days=7)
            elif schedule_type == 'biweekly':
                current_end = current_start + timedelta(days=13)
                next_start = current_start + timedelta(days=14)
            elif schedule_type == 'semi_monthly':
                if current_start.day <= 15:
                    current_end = current_start.replace(day=15)
                    next_start = current_start.replace(day=16)
                else:
                    # End of month
                    next_month = current_start + relativedelta(months=1, day=1)
                    current_end = next_month - timedelta(days=1)
                    next_start = next_month
            elif schedule_type == 'monthly':
                next_month = current_start + relativedelta(months=1, day=1)
                current_end = next_month - timedelta(days=1)
                next_start = next_month
            else:
                raise ValidationError(_('Unknown schedule type: %s') % schedule_type)
            
            pay_date = current_end + timedelta(days=pay_day_offset)
            
            # Check if period already exists
            existing = self.search([
                ('company_id', '=', company_id),
                ('schedule_type', '=', schedule_type),
                ('date_start', '=', current_start),
            ], limit=1)
            
            if not existing:
                period = self.create({
                    'company_id': company_id,
                    'schedule_type': schedule_type,
                    'date_start': current_start,
                    'date_end': current_end,
                    'pay_date': pay_date,
                })
                periods.append(period)
            
            current_start = next_start
        
        return periods

    @api.model
    def get_current_period(self, company_id, schedule_type):
        """Get the current active pay period."""
        today = fields.Date.context_today(self)
        period = self.search([
            ('company_id', '=', company_id),
            ('schedule_type', '=', schedule_type),
            ('date_start', '<=', today),
            ('date_end', '>=', today),
        ], limit=1)
        return period

    @api.model
    def get_available_periods(self, company_id, schedule_type, include_past_months=6, include_future_months=6):
        """
        Get list of available pay periods for selection.
        Ordered: Current period first, then future periods, then past periods.
        """
        today = fields.Date.context_today(self)
        
        # Calculate date range
        past_date = today - relativedelta(months=include_past_months)
        future_date = today + relativedelta(months=include_future_months)
        
        # Get all periods within range
        all_periods = self.search([
            ('company_id', '=', company_id),
            ('schedule_type', '=', schedule_type),
            ('date_start', '>=', past_date),
            ('date_end', '<=', future_date),
        ])
        
        # Separate into current, future, and past
        current = all_periods.filtered(lambda p: p.date_start <= today <= p.date_end)
        future = all_periods.filtered(lambda p: p.date_start > today).sorted('date_start')
        past = all_periods.filtered(lambda p: p.date_end < today).sorted('date_start', reverse=True)
        
        # Combine: current first, then future (ascending), then past (descending)
        return current + future + past
    
    @api.model
    def auto_generate_periods_if_needed(self, company_id, schedule_type):
        """
        Automatically generate pay periods for past 6 months and future 6 months if not exist.
        """
        settings = self.env['payroll.pay.period.settings'].get_or_create_settings(company_id)
        today = fields.Date.context_today(self)
        
        # Calculate how far back and forward we need
        past_date = today - relativedelta(months=6)
        future_date = today + relativedelta(months=6)
        
        # Find the first period start date aligned to schedule
        first_start = settings.first_period_start
        
        # If first_period_start is in the future, work backwards
        if first_start > past_date:
            # Calculate periods going backwards
            periods_to_generate = []
            current_start = first_start
            
            # Go backwards to cover past 6 months
            while current_start > past_date:
                if schedule_type == 'weekly':
                    current_start = current_start - timedelta(days=7)
                elif schedule_type == 'biweekly':
                    current_start = current_start - timedelta(days=14)
                elif schedule_type == 'semi_monthly':
                    if current_start.day <= 15:
                        # Go to previous month's 16th
                        prev_month = current_start - relativedelta(months=1)
                        current_start = prev_month.replace(day=16)
                    else:
                        current_start = current_start.replace(day=1)
                elif schedule_type == 'monthly':
                    current_start = current_start - relativedelta(months=1)
            
            first_start = current_start
        
        # Now generate from that start date forward
        periods_needed = 0
        temp_start = first_start
        while temp_start <= future_date:
            periods_needed += 1
            if schedule_type == 'weekly':
                temp_start = temp_start + timedelta(days=7)
            elif schedule_type == 'biweekly':
                temp_start = temp_start + timedelta(days=14)
            elif schedule_type == 'semi_monthly':
                if temp_start.day <= 15:
                    temp_start = temp_start.replace(day=16)
                else:
                    temp_start = temp_start + relativedelta(months=1, day=1)
            elif schedule_type == 'monthly':
                temp_start = temp_start + relativedelta(months=1)
        
        # Generate the periods
        self.generate_periods(
            company_id=company_id,
            schedule_type=schedule_type,
            start_date=first_start,
            num_periods=periods_needed,
            pay_day_offset=settings.pay_day_offset,
        )


class PayrollPayPeriodSettings(models.Model):
    """
    Pay Period Settings per Company
    Stores the payroll schedule configuration.
    """
    _name = 'payroll.pay.period.settings'
    _description = 'Pay Period Settings'

    company_id = fields.Many2one(
        'res.company',
        string='Company',
        required=True,
        default=lambda self: self.env.company,
        ondelete='cascade',
    )
    
    schedule_type = fields.Selection([
        ('weekly', 'Weekly'),
        ('biweekly', 'Bi-Weekly'),
        ('semi_monthly', 'Semi-Monthly'),
        ('monthly', 'Monthly'),
    ], string='Pay Schedule', required=True, default='biweekly')
    
    first_period_start = fields.Date(
        string='First Period Start',
        required=True,
        help='Start date of the first pay period',
    )
    
    pay_day_offset = fields.Integer(
        string='Days Until Pay Date',
        default=7,
        help='Number of days after period end until pay date',
    )
    
    auto_generate_periods = fields.Boolean(
        string='Auto-Generate Periods',
        default=True,
        help='Automatically generate future pay periods',
    )
    
    periods_to_generate = fields.Integer(
        string='Periods to Generate',
        default=12,
        help='Number of future periods to keep generated',
    )

    _sql_constraints = [
        ('unique_company', 'unique(company_id)', 'Only one pay period settings record per company.'),
    ]

    @api.model
    def get_or_create_settings(self, company_id=None):
        """Get or create settings for a company."""
        if not company_id:
            company_id = self.env.company.id
        
        settings = self.search([('company_id', '=', company_id)], limit=1)
        if not settings:
            # Create with sensible defaults
            today = fields.Date.context_today(self)
            # Default to start of current bi-weekly period (Monday)
            first_start = today - timedelta(days=today.weekday())
            settings = self.create({
                'company_id': company_id,
                'schedule_type': 'biweekly',
                'first_period_start': first_start,
            })
        return settings

    def action_generate_periods(self):
        """Generate pay periods based on settings."""
        self.ensure_one()
        periods = self.env['payroll.pay.period'].generate_periods(
            company_id=self.company_id.id,
            schedule_type=self.schedule_type,
            start_date=self.first_period_start,
            num_periods=self.periods_to_generate,
            pay_day_offset=self.pay_day_offset,
        )
        
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Pay Periods Generated'),
                'message': _('%d pay periods have been generated.') % len(periods),
                'type': 'success',
            },
        }
