<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use App\Services\ApiClient;
use App\Exceptions\SchoolYear\SchoolYearConfigurationException;
use App\Support\SchoolYear\SchoolYearContext;
use Throwable;

/**
 * Class BaseController
 *
 * BaseController provides a convenient place for loading components
 * and performing functions that are needed by all your controllers.
 * Extend this class in any new controllers:
 *     class Home extends BaseController
 *
 * For security be sure to declare any new methods as protected or private.
 */
abstract class BaseController extends Controller
{
    /**
     * Instance of the main Request object.
     *
     * @var CLIRequest|IncomingRequest
     */
    protected $request;

    /**
     * An array of helpers to be loaded automatically upon
     * class instantiation. These helpers will be available
     * to all other controllers that extend BaseController.
     *
     * @var list<string>
     */
    protected $helpers = ['security'];

    /** @var ApiClient */
    protected ApiClient $api;

    /**
     * Be sure to declare properties for any property fetch you initialized.
     * The creation of dynamic property is deprecated in PHP 8.2.
     */
    // protected $session;

    /**
     * @return void
     */
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        // Do Not Edit This Line
        parent::initController($request, $response, $logger);

        // Preload any models, libraries, etc, here.
        // E.g.: $this->session = \Config\Services::session();
        session();
        $this->syncSchoolYearPropertyFromContext();
        $this->injectSchoolYearViewData();

        // Shared API client available to all controllers
        $this->api = service('apiClient');
    }

    /**
     * Get the user role from the session or database
     *
     * @return string|null
     */
    protected function getUserRole()
    {
        // Assuming the user role is stored in the session
        return session()->get('role');
    }

    protected function validated(array $rules, ?array $data = null): array
    {
        $validation = service('validation');
        $payload = $data ?? ($this->request->getJSON(true) ?: $this->request->getPost());
        $payload = sanitize_request_value($payload);

        $validation->setRules($rules);

        if (! $validation->run($payload)) {
            throw new \InvalidArgumentException(json_encode($validation->getErrors(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: 'Validation failed');
        }

        return $payload;
    }

    protected function resolveSchoolYearContext(?int $routeSchoolYearId = null): SchoolYearContext
    {
        try {
            return service('schoolYearContext')->resolve($this->request, $routeSchoolYearId);
        } catch (Throwable $e) {
            log_message('warning', 'School-year resolution failed: {message}', [
                'message' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    protected function currentSchoolYearName(?string $fallback = null): string
    {
        try {
            return $this->resolveSchoolYearContext()->yearName();
        } catch (Throwable $e) {
            if ($fallback !== null && $fallback !== '') {
                return $fallback;
            }

            try {
                return (string) ((new \App\Models\ConfigurationModel())->getConfig('school_year') ?? '');
            } catch (Throwable) {
                return '';
            }
        }
    }

    protected function assertSchoolYearWritable(
        SchoolYearContext $context,
        bool $allowDraftForAdmin = false,
        bool $isAdmin = false
    ): void {
        service('schoolYearWriteGuard')->assertWritable($context, $allowDraftForAdmin, $isAdmin);
    }

    private function syncSchoolYearPropertyFromContext(): void
    {
        if (! property_exists($this, 'schoolYear')) {
            return;
        }

        if (! $this->request instanceof IncomingRequest || is_cli()) {
            return;
        }

        if (! session()->get('user_id')) {
            return;
        }

        try {
            $this->schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
        } catch (Throwable $e) {
            log_message('warning', 'Unable to sync controller school-year property: {message}', [
                'message' => $e->getMessage(),
            ]);
        }
    }

    private function injectSchoolYearViewData(): void
    {
        if (! config('Feature')->globalSchoolYearSelector) {
            return;
        }

        if (! $this->request instanceof IncomingRequest || is_cli()) {
            return;
        }

        if (! session()->get('user_id')) {
            return;
        }

        if (! $this->shouldShowSchoolYearSelector()) {
            return;
        }

        $accept = strtolower($this->request->getHeaderLine('Accept'));
        if ($this->request->isAJAX() || str_contains($accept, 'application/json')) {
            return;
        }

        try {
            service('renderer')->setData(service('schoolYearViewData')->forCurrentRequest($this->request));
        } catch (SchoolYearConfigurationException $e) {
            throw $e;
        } catch (Throwable $e) {
            log_message('warning', 'Unable to inject school-year view data: {message}', [
                'message' => $e->getMessage(),
            ]);
        }
    }

    private function shouldShowSchoolYearSelector(): bool
    {
        $roles = (array) session()->get('roles');
        $singleRole = session()->get('role');
        if ($singleRole !== null && $singleRole !== '') {
            $roles[] = $singleRole;
        }

        $roles = array_map(
            static fn ($role): string => strtolower(trim((string) $role)),
            $roles
        );

        return (bool) array_intersect($roles, [
            'admin',
            'administrator',
            'administrative staff',
            'parent',
            'principal',
            'teacher',
            'teacher assistant',
            'teacher_assistant',
            'vice principal',
        ]);
    }
}

?>
