<?php

namespace App\Services\Employee;

use App\Services\BaseService;
use App\Repositories\HolidayRepository;
use App\Models\Holiday;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class HolidayService extends BaseService
{
    protected HolidayRepository $holidayRepository;

    public function __construct(HolidayRepository $repository)
    {
        parent::__construct($repository);
        $this->holidayRepository = $repository;
    }

    /**
     * Get all active holidays
     */
    public function getActive(): Collection
    {
        return $this->holidayRepository->getActive();
    }

    /**
     * Get holidays for a year
     */
    public function getForYear(int $year): Collection
    {
        return $this->holidayRepository->getForYear($year);
    }

    /**
     * Get holidays for current year
     */
    public function getCurrentYear(): Collection
    {
        return $this->holidayRepository->getCurrentYear();
    }

    /**
     * Get holidays by type
     */
    public function getByType(string $type): Collection
    {
        return $this->holidayRepository->getByType($type);
    }

    /**
     * Get holidays for a month
     */
    public function getForMonth(int $month, int $year): Collection
    {
        return $this->holidayRepository->getForMonth($month, $year);
    }

    /**
     * Get holidays in date range
     */
    public function getInRange(string $start, string $end): Collection
    {
        return $this->holidayRepository->getInRange($start, $end);
    }

    /**
     * Get upcoming holidays
     */
    public function getUpcoming(int $limit = 5): Collection
    {
        return $this->holidayRepository->getUpcoming($limit);
    }

    /**
     * Check if date is a holiday
     */
    public function isHoliday(string $date): bool
    {
        return $this->holidayRepository->isHoliday($date);
    }

    /**
     * Count holidays in range
     */
    public function countInRange(string $start, string $end): int
    {
        return $this->holidayRepository->countInRange($start, $end);
    }

    /**
     * Create a new holiday
     */
    public function createHoliday(array $data): Holiday
    {
        return DB::transaction(function () use ($data) {
            // Set defaults
            $data['status'] = $data['status'] ?? Holiday::STATUS_ACTIVE;

            // Check for duplicate date
            $existing = $this->holidayRepository->findByDate($data['date']);
            if ($existing) {
                throw new \Exception('A holiday already exists on this date.');
            }

            $this->logAction('Holiday created', [
                'name' => $data['name'],
                'date' => $data['date'],
            ]);

            return $this->create($data);
        });
    }

    /**
     * Update a holiday
     */
    public function updateHoliday(int $id, array $data): Holiday
    {
        return DB::transaction(function () use ($id, $data) {
            // Check for duplicate date if date is being changed
            if (!empty($data['date'])) {
                $existing = $this->holidayRepository->findByDate($data['date']);
                if ($existing && $existing->id !== $id) {
                    throw new \Exception('A holiday already exists on this date.');
                }
            }

            $this->logAction('Holiday updated', ['id' => $id]);

            return $this->update($id, $data);
        });
    }

    /**
     * Toggle holiday status
     */
    public function toggleStatus(int $id): Holiday
    {
        return DB::transaction(function () use ($id) {
            $holiday = $this->findByIdOrFail($id);
            $newStatus = $holiday->status === Holiday::STATUS_ACTIVE
                ? Holiday::STATUS_INACTIVE
                : Holiday::STATUS_ACTIVE;

            $holiday->update(['status' => $newStatus]);

            $this->logAction('Holiday status toggled', [
                'id' => $id,
                'new_status' => $newStatus,
            ]);

            return $holiday->fresh();
        });
    }

    /**
     * Get yearly summary
     */
    public function getYearlySummary(int $year): array
    {
        $holidays = $this->getForYear($year);

        return [
            'total' => $holidays->count(),
            'by_type' => [
                Holiday::TYPE_PUBLIC => $holidays->where('type', Holiday::TYPE_PUBLIC)->count(),
                Holiday::TYPE_OPTIONAL => $holidays->where('type', Holiday::TYPE_OPTIONAL)->count(),
                Holiday::TYPE_COMPANY => $holidays->where('type', Holiday::TYPE_COMPANY)->count(),
                Holiday::TYPE_RESTRICTED => $holidays->where('type', Holiday::TYPE_RESTRICTED)->count(),
            ],
            'by_month' => $this->getHolidaysByMonth($holidays),
            'upcoming_count' => $holidays->filter(fn($h) => $h->date->isFuture())->count(),
            'past_count' => $holidays->filter(fn($h) => $h->date->isPast())->count(),
        ];
    }

    /**
     * Get holidays grouped by month
     */
    protected function getHolidaysByMonth(Collection $holidays): array
    {
        $byMonth = [];
        for ($i = 1; $i <= 12; $i++) {
            $byMonth[$i] = $holidays->filter(fn($h) => $h->date->month === $i)->count();
        }
        return $byMonth;
    }

    /**
     * Get holiday dates as array for a year
     */
    public function getDatesForYear(int $year): array
    {
        return $this->getForYear($year)
            ->pluck('date')
            ->map(fn($d) => $d->toDateString())
            ->toArray();
    }

    /**
     * Import holidays from array
     */
    public function importHolidays(array $holidays): array
    {
        $results = ['success' => 0, 'failed' => 0, 'errors' => []];

        DB::transaction(function () use ($holidays, &$results) {
            foreach ($holidays as $index => $holiday) {
                try {
                    $this->createHoliday($holiday);
                    $results['success']++;
                } catch (\Exception $e) {
                    $results['failed']++;
                    $results['errors'][] = "Row {$index}: {$e->getMessage()}";
                }
            }
        });

        return $results;
    }

    /**
     * Get next holiday
     */
    public function getNextHoliday(): ?Holiday
    {
        return Holiday::getNextHoliday();
    }
}
