<?php

namespace App\Repositories;

use App\Repositories\Contracts\RepositoryInterface;
use App\Services\Support\TenantContext;
use CodeIgniter\Model;

/**
 * Base for every tenant-scoped Repository. Subclasses set $tenantColumn
 * (default 'agency_id') and $model. Every query method here funnels through
 * scopedBuilder(), which injects "WHERE {tenantColumn} = currentAgencyId"
 * unless the caller is inside TenantContext::runWithoutTenantScope().
 *
 * Repositories for platform-global tables (e.g. skills, job_categories,
 * subscription_plans) should extend this with $tenantColumn = null to skip
 * scoping entirely — an explicit opt-out, not an accidental omission.
 */
abstract class BaseRepository implements RepositoryInterface
{
    protected Model $model;
    protected ?string $tenantColumn = 'agency_id';
    protected TenantContext $tenant;

    public function __construct(Model $model)
    {
        $this->model = $model;
        $this->tenant = TenantContext::getInstance();
    }

    protected function scopedBuilder()
    {
        $builder = $this->model->builder();

        if ($this->tenantColumn === null) {
            return $builder; // platform-global table, no scoping applies
        }

        if ($this->tenant->isBypassActive()) {
            return $builder; // Super Admin explicit cross-tenant query
        }

        $agencyId = $this->tenant->getAgencyId();

        if ($agencyId === null) {
            // No tenant context resolved (e.g. candidate user, or public
            // endpoint) — return a builder that can never match a row,
            // rather than silently leaking all tenants' data.
            return $builder->where('1 = 0');
        }

        return $builder->where($this->model->table . '.' . $this->tenantColumn, $agencyId);
    }

    public function find(int $id): ?object
    {
        $row = $this->scopedBuilder()->where('id', $id)->get()->getFirstRow();
        return $row ? $this->model->find($id) : null; // rehydrate via model for casts/entity
    }

    public function all(array $filters = [], int $page = 1, int $perPage = 20): array
    {
        $builder = $this->scopedBuilder();

        foreach ($filters as $column => $value) {
            $builder->where($column, $value);
        }

        $offset = ($page - 1) * $perPage;
        return $builder->limit($perPage, $offset)->get()->getResultArray();
    }

    public function create(array $data): object
    {
        if ($this->tenantColumn !== null && ! isset($data[$this->tenantColumn]) && ! $this->tenant->isBypassActive()) {
            $data[$this->tenantColumn] = $this->tenant->getAgencyId();
        }

        $id = $this->model->insert($data, true);
        return $this->model->find($id);
    }

    public function update(int $id, array $data): bool
    {
        // Ensure the target row is actually within scope before allowing
        // the write — prevents an agency updating another tenant's row by
        // guessing an ID, even if the controller forgot to re-check.
        if (! $this->find($id)) {
            return false;
        }

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

    public function delete(int $id): bool
    {
        if (! $this->find($id)) {
            return false;
        }

        return $this->model->delete($id);
    }

    public function count(array $filters = []): int
    {
        $builder = $this->scopedBuilder();
        foreach ($filters as $column => $value) {
            $builder->where($column, $value);
        }
        return $builder->countAllResults();
    }
}
