<?php

namespace App\Services\Payroll;

use App\Models\PayrollCycle;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class PayrollCycleService
{
    public function createOrOpen(int $companyId, int $year, int $month): PayrollCycle
    {
        $start = Carbon::createFromDate($year, $month, 1)->startOfDay();
        $end = $start->copy()->endOfMonth();

        $cycle = PayrollCycle::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('year', $year)
            ->where('month', $month)
            ->first();

        if ($cycle) {
            return $cycle;
        }

        return PayrollCycle::withoutGlobalScopes()->create([
            'company_id' => $companyId,
            'month' => $month,
            'year' => $year,
            'status' => 'draft',
            'start_date' => $start,
            'end_date' => $end,
        ]);
    }

    public function ensureNotLocked(PayrollCycle $cycle): void
    {
        if ($cycle->isLocked()) {
            throw new \RuntimeException('Payroll cycle is locked. No edits or recalculations allowed.');
        }
    }

    public function ensureRunnable(PayrollCycle $cycle): void
    {
        $this->ensureNotLocked($cycle);
        $now = now();
        if ((int) $cycle->year > $now->year
            || ((int) $cycle->year === $now->year && (int) $cycle->month > $now->month)) {
            throw new \RuntimeException('Cannot run payroll for a future month.');
        }
    }

    public function transitionToProcessing(PayrollCycle $cycle): PayrollCycle
    {
        $this->ensureNotLocked($cycle);
        $cycle->update(['status' => 'processing']);
        return $cycle->fresh();
    }

    public function transitionToDraft(PayrollCycle $cycle): PayrollCycle
    {
        $this->ensureNotLocked($cycle);
        $cycle->update(['status' => 'draft']);
        return $cycle->fresh();
    }

    public function transitionToCompleted(PayrollCycle $cycle, ?int $completedBy = null): PayrollCycle
    {
        $this->ensureNotLocked($cycle);
        $cycle->update([
            'status' => 'completed',
            'completed_at' => now(),
            'completed_by' => $completedBy,
        ]);
        return $cycle->fresh();
    }

    public function lock(PayrollCycle $cycle, ?int $lockedBy = null): PayrollCycle
    {
        if ($cycle->status !== 'completed' && $cycle->status !== 'draft') {
            $cycle->update(['status' => 'completed', 'completed_at' => $cycle->completed_at ?? now(), 'completed_by' => $cycle->completed_by ?? $lockedBy]);
        }
        $cycle->update([
            'status' => 'locked',
            'locked_at' => now(),
            'locked_by' => $lockedBy,
        ]);
        return $cycle->fresh();
    }

    public function unlock(PayrollCycle $cycle): PayrollCycle
    {
        $cycle->update([
            'status' => 'completed',
            'locked_at' => null,
            'locked_by' => null,
        ]);
        return $cycle->fresh();
    }

    /**
     * Get cycle for company/year/month or null.
     */
    public function getCycle(int $companyId, int $year, int $month): ?PayrollCycle
    {
        return PayrollCycle::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('year', $year)
            ->where('month', $month)
            ->first();
    }

    /**
     * Month statuses for a year: completed/locked = green, current = orange, future = grey, else pending.
     *
     * @return array<int, string> month (1-12) => status
     */
    public function getMonthStatuses(int $companyId, int $year): array
    {
        $currentYear = (int) date('Y');
        $currentMonth = (int) date('n');
        $statuses = [];
        for ($m = 1; $m <= 12; $m++) {
            if ($year > $currentYear || ($year === $currentYear && $m > $currentMonth)) {
                $statuses[$m] = 'future';
                continue;
            }
            $cycle = $this->getCycle($companyId, $year, $m);
            if (! $cycle) {
                $statuses[$m] = 'pending';
                continue;
            }
            $statuses[$m] = in_array($cycle->status, ['completed', 'locked'], true) ? 'completed' : ($year === $currentYear && $m === $currentMonth ? 'current' : 'pending');
        }
        return $statuses;
    }
}
