<?php

namespace App\Services\System;

use App\Models\Configuration;
use DateTimeImmutable;
use DateTimeZone;
use Illuminate\Support\Facades\Log;

class ConfigUpdateService
{
    private array $tasks = [
        'update_date_age_reference' => 'taskUpdateDateAgeReference',
        'enable_attendance_on' => 'taskEnableAttendanceOn',
        'enable_attendance_off' => 'taskEnableAttendanceOff',
        'set_semester_spring' => 'taskSetSemesterSpring',
        'set_semester_fall' => 'taskSetSemesterFall',
    ];

    public function availableTasks(): array
    {
        return array_keys($this->tasks);
    }

    public function runTask(string $task, bool $dry, bool $force, DateTimeZone $tz): array
    {
        if (!isset($this->tasks[$task])) {
            return ['ok' => false, 'message' => 'Unknown task.'];
        }

        $lockFile = storage_path('app/locks/config_update_' . $task . '.lock');
        if (!is_dir(dirname($lockFile))) {
            @mkdir(dirname($lockFile), 0775, true);
        }

        $fp = @fopen($lockFile, 'c+');
        if (!$fp) {
            return ['ok' => false, 'message' => 'Unable to open lock file.'];
        }

        if (!$force && !flock($fp, LOCK_EX | LOCK_NB)) {
            fclose($fp);
            return ['ok' => false, 'message' => 'Task is already running.'];
        }

        try {
            $method = $this->tasks[$task];
            $ok = (bool) $this->{$method}($dry, $force, $tz);
            return ['ok' => $ok, 'task' => $task];
        } catch (\Throwable $e) {
            Log::error('ConfigUpdate task failed: ' . $e->getMessage());
            return ['ok' => false, 'message' => $e->getMessage()];
        } finally {
            try {
                flock($fp, LOCK_UN);
                fclose($fp);
                @unlink($lockFile);
            } catch (\Throwable $e) {
            }
        }
    }

    private function taskEnableAttendanceOn(bool $dry, bool $force, DateTimeZone $tz): bool
    {
        return $this->setConfig('enable_attendance', '1', $dry);
    }

    private function taskEnableAttendanceOff(bool $dry, bool $force, DateTimeZone $tz): bool
    {
        return $this->setConfig('enable_attendance', '0', $dry);
    }

    private function taskUpdateDateAgeReference(bool $dry, bool $force, DateTimeZone $tz): bool
    {
        $now = new DateTimeImmutable('now', $tz);
        $isJune1 = $now->format('n-j') === '6-1';
        if (!$isJune1 && !$force) {
            return true;
        }

        $value = $now->format('Y') . '-12-31';
        return $dry ? true : Configuration::setConfigValueByKey('date_age_reference', $value);
    }

    private function taskSetSemesterSpring(bool $dry, bool $force, DateTimeZone $tz): bool
    {
        $now = new DateTimeImmutable('now', $tz);
        $isFeb1 = $now->format('n-j') === '2-1';
        if (!$isFeb1 && !$force) {
            return true;
        }

        return $dry ? true : Configuration::setConfigValueByKey('semester', 'Spring');
    }

    private function taskSetSemesterFall(bool $dry, bool $force, DateTimeZone $tz): bool
    {
        $now = new DateTimeImmutable('now', $tz);
        $isJun1 = $now->format('n-j') === '6-1';
        if (!$isJun1 && !$force) {
            return true;
        }

        return $dry ? true : Configuration::setConfigValueByKey('semester', 'Fall');
    }

    private function setConfig(string $key, string $value, bool $dry): bool
    {
        if ($dry) {
            return true;
        }

        return Configuration::setConfigValueByKey($key, $value);
    }
}
