<?php

namespace App\Controllers;

use App\Services\CompanyScopeService;
use App\Services\PaymentService;
use App\Services\RbacService;
use App\Services\WorkflowGuardService;
use App\Services\WorkOrderWorkflowService;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;

/**
 * BaseController — extended by all app controllers.
 */
abstract class BaseController extends Controller
{
    /** @var \CodeIgniter\Database\BaseConnection */
    protected $db;

    /** @var array<string, string> */
    protected array $settings = [];

    protected $helpers = ['form', 'url', 'text', 'date', 'number', 'pagination'];

    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);

        $this->db = \Config\Database::connect();

        try {
            $rows = $this->db->table('system_settings')->get()->getResultArray();
            foreach ($rows as $row) {
                $this->settings[$row['setting_key']] = $row['setting_value'];
            }
        } catch (\Throwable $e) {
            $this->settings = [];
        }

        // Ensure defaults for branding so views never have null
        $this->settings['company_name']    = $this->settings['company_name']    ?? 'FM ERP';
        $this->settings['company_tagline'] = $this->settings['company_tagline'] ?? 'Facility Management ERP';
        $this->settings['primary_color']   = $this->settings['primary_color']   ?? '#76002b';
        $this->settings['secondary_color'] = $this->settings['secondary_color'] ?? '#c7ba9a';
        $this->settings['company_logo']    = $this->settings['company_logo']    ?? '';
        $this->settings['currency']        = $this->settings['currency']        ?? 'QAR';
        $this->settings['vat_enabled']     = $this->settings['vat_enabled']     ?? '0';
        $this->settings['vat_rate']        = $this->settings['vat_rate']        ?? '5';
    }

    /**
     * Returns the public URL of the company logo (or null if none uploaded).
     */
    protected function logoUrl(): ?string
    {
        $logo = $this->settings['company_logo'] ?? '';
        if ($logo && file_exists(WRITEPATH . 'uploads/' . $logo)) {
            return base_url('uploads/' . $logo);
        }
        return null;
    }

    /**
     * Returns the logo as a base64 data URI for PDF/print embedding.
     */
    protected function logoBase64(): ?string
    {
        $logo = $this->settings['company_logo'] ?? '';
        $path = WRITEPATH . 'uploads/' . $logo;
        if ($logo && file_exists($path)) {
            $mime = mime_content_type($path);
            return 'data:' . $mime . ';base64,' . base64_encode(file_get_contents($path));
        }
        return null;
    }

    /**
     * Build view data. Includes ALL common variables every view may need.
     */
    protected function viewData(array $data = []): array
    {
        $uid  = session()->get('user_id');
        $role = session()->get('user_role');

        $currentUser = [
            'id'           => $uid,
            'name'         => session()->get('user_name') ?? 'User',
            'email'        => session()->get('user_email') ?? '',
            'role_name'    => $role ?? 'client',
            'role_display' => session()->get('role_display') ?? '',
        ];

        $unreadCount = 0;
        if ($uid) {
            try {
                $unreadCount = $this->db->table('notifications')
                    ->where('user_id', $uid)
                    ->where('is_read', 0)
                    ->countAllResults();
            } catch (\Throwable $e) {
                $unreadCount = 0;
            }
        }

        $vatEnabled = ($this->settings['vat_enabled'] ?? '0') === '1';
        $vatRate    = (float) ($this->settings['vat_rate'] ?? 5);
        $currency   = $this->settings['currency'] ?? 'QAR';

        $companies = [];
        if (($currentUser['role_name'] ?? '') === 'super_admin') {
            try {
                $companies = $this->companyScope()->activeCompanies();
            } catch (\Throwable $e) {
                $companies = [];
            }
        }

        $rbac = new RbacService($this->db);

        return array_merge([
            'settings'       => $this->settings,
            'currentUser'    => $currentUser,
            'unreadCount'    => $unreadCount,
            'currency'       => $currency,
            'vatEnabled'     => $vatEnabled,
            'vatRate'        => $vatRate,
            'user_name'      => $currentUser['name'],
            'user_role'      => $currentUser['role_name'],
            'user_id'        => $uid,
            'company_id'     => session()->get('company_id'),
            'companies'      => $companies,
            'companyLogoUrl' => $this->logoUrl(),
            'companyLogoB64' => $this->logoBase64(),
            'rbac'           => $rbac,
        ], $data);
    }

    /**
     * Generate next sequential number: PREFIX-YEAR-NNNN
     */
    protected function generateNumber(string $prefix, string $table, string $column): string
    {
        $year = date('Y');
        $like = $prefix . '-' . $year . '-';

        $last = $this->db->table($table)
            ->like($column, $like, 'after')
            ->orderBy('id', 'DESC')
            ->limit(1)
            ->get()
            ->getRowArray();

        $seq = $last ? ((int) end(explode('-', $last[$column])) + 1) : 1;

        return $prefix . '-' . $year . '-' . sprintf('%04d', $seq);
    }

    /**
     * Redirect with error if role not allowed.
     */
    protected function requireRole(string ...$roles): void
    {
        if (! in_array(session()->get('user_role'), $roles, true)) {
            session()->setFlashdata('error', 'Access denied.');
            redirect()->to(base_url('dashboard'))->send();
            exit;
        }
    }

    /**
     * Safe pagination from request query (?page=).
     *
     * @return array{page: int, perPage: int, offset: int}
     */
    protected function paginate(int $perPage = 25): array
    {
        return pagination_params(
            (int) ($this->request->getGet('page') ?? 1),
            $perPage
        );
    }

    /**
     * Active users filtered by role name (never hardcode role_id).
     *
     * @return list<array<string, mixed>>
     */
    protected function usersByRole(string $roleName): array
    {
        return $this->db->table('users u')
            ->select('u.id, u.name, u.email, u.phone')
            ->join('roles r', 'r.id = u.role_id')
            ->where('r.name', $roleName)
            ->where('u.status', 'active')
            ->orderBy('u.name', 'ASC')
            ->get()
            ->getResultArray();
    }

    /** Resolve role ID from roles.name (no hardcoded IDs). */
    protected function roleIdByName(string $roleName): ?int
    {
        $row = $this->db->table('roles')->select('id')->where('name', $roleName)->get()->getRowArray();

        return $row ? (int) $row['id'] : null;
    }

    protected function companyScope(): CompanyScopeService
    {
        return new CompanyScopeService($this->db, session());
    }

    protected function workflowService(): WorkOrderWorkflowService
    {
        return new WorkOrderWorkflowService($this->db, $this->settings);
    }

    protected function workflowGuard(): WorkflowGuardService
    {
        return new WorkflowGuardService($this->db);
    }

    protected function paymentService(): PaymentService
    {
        return new PaymentService($this->db);
    }

    /** Universal tenant filter for any module table. */
    protected function scopeTable($builder, string $table, string $facilityColumn = 'facility_id')
    {
        return $this->companyScope()->scopeTable($builder, $table, $facilityColumn);
    }

    /**
     * Scope query builder by session company when set (multi-company SaaS).
     */
    protected function scopeCompany($builder, string $column = 'company_id')
    {
        return $this->companyScope()->applyCompanyColumn($builder, $column);
    }

    /** Restrict to facilities belonging to the active company. */
    protected function scopeFacilities($builder, string $facilityColumn = 'facility_id')
    {
        return $this->companyScope()->applyFacilityScope($builder, $facilityColumn);
    }

    protected function assertFacilityAccess(int $facilityId): void
    {
        if (!$this->companyScope()->canAccessFacility($facilityId)) {
            session()->setFlashdata('error', 'Access denied for this facility.');
            redirect()->to(base_url('dashboard'))->send();
            exit;
        }
    }

    protected function assertWorkOrderAccess(int $woId): void
    {
        if (!$this->companyScope()->canAccessWorkOrder($woId)) {
            session()->setFlashdata('error', 'Access denied for this work order.');
            redirect()->to(base_url('workorders'))->send();
            exit;
        }
    }

    protected function countScopedMaintenanceRequests(?string $status = null): int
    {
        $q = $this->db->table('maintenance_requests r');
        $this->scopeFacilities($q, 'r.facility_id');
        if ($status !== null) {
            $q->where('r.status', $status);
        }

        return $q->countAllResults();
    }

    /**
     * SQL fragment for raw queries: AND alias.facility_id IN (...).
     *
     * @return array{0: string, 1: list<int>}
     */
    protected function sqlFacilityFilter(string $tableAlias, string $column = 'facility_id'): array
    {
        $ids = $this->companyScope()->facilityIds();
        if ($ids === null) {
            return ['', []];
        }
        if ($ids === []) {
            return [' AND 1=0', []];
        }
        $placeholders = implode(',', array_fill(0, count($ids), '?'));

        return [" AND {$tableAlias}.{$column} IN ({$placeholders})", $ids];
    }

    /**
     * Write audit trail to activity_logs.
     */
    protected function logActivity(
        string $action,
        string $module,
        ?int $recordId = null,
        ?string $description = null
    ): void {
        $uid = session()->get('user_id');
        if (!$uid) {
            return;
        }

        try {
            $this->db->table('activity_logs')->insert([
                'user_id'     => $uid,
                'action'      => $action,
                'module'      => $module,
                'record_id'   => $recordId,
                'description' => $description,
                'ip_address'  => $this->request->getIPAddress(),
            ]);
        } catch (\Throwable $e) {
            log_message('error', 'activity_log failed: ' . $e->getMessage());
        }
    }
}
