<?php

declare(strict_types=1);

namespace BranchAdmin\Controllers;

use App\Core\Controller;

/**
 * Branch Admin Base Controller
 *
 * Abstract base for all Branch Admin panel controllers.
 *
 * The Branch Admin panel is at /branch/* — separate from the existing
 * /admin/* panel (which manages website frontage) and from /super-admin/*
 * (which manages branches/branch admins system-wide).
 *
 * Each branch_admin user is assigned to exactly ONE branch (stored in
 * users.branch_id, mirrored in session as $_SESSION['branch_id']).
 * Every query in branch controllers MUST filter by this branch_id so
 * branch admins only see their own data.
 */
abstract class BranchBaseController extends Controller
{
    /** Base path for Branch Admin view files */
    private const VIEW_PATH = APP_ROOT . '/branch/Views/';

    /**
     * Render a Branch Admin view wrapped in the Branch layout.
     *
     * Auto-injects: csrfToken, currentUser, flash messages, title, currentPage,
     * branchId, branchCode, branchName (from session).
     *
     * @param string $view View path relative to /branch/Views/ (e.g. 'dashboard', 'profile/index')
     * @param array<string, mixed> $data Data to extract into the view
     * @return never
     */
    protected function branchAdminView(string $view, array $data = []): never
    {
        extract($data, EXTR_SKIP);

        // Flash messages
        $flash_error   = flash('error');
        $flash_success = flash('success');
        $flash_warning = flash('warning');
        $flash_info    = flash('info');

        // CSRF token
        $csrfToken = generateCsrfToken();

        // Current user (from session)
        $currentUser = getCurrentUser();

        // Branch context from session (always present for branch_admin)
        $branchId   = (int) ($_SESSION['branch_id']   ?? 0);
        $branchCode =       ($_SESSION['branch_code'] ?? '');

        // Fetch branch name (cached per request via static)
        static $branchName = null;
        if ($branchName === null && $branchId > 0) {
            try {
                $stmt = $this->db->prepare("SELECT `name`, `wallet_balance`, `status` FROM `branches` WHERE `id` = :bid LIMIT 1");
                $stmt->execute([':bid' => $branchId]);
                $b = $stmt->fetch();
                $branchName = $b['name'] ?? '';
                $branchWallet = (float) ($b['wallet_balance'] ?? 0);
                $branchStatus = $b['status'] ?? 'pending';
            } catch (\Throwable $e) {
                $branchName = '';
                $branchWallet = 0.0;
                $branchStatus = 'pending';
            }
        }
        $branchName   = $branchName   ?? '';
        $branchWallet = $branchWallet ?? 0.0;
        $branchStatus = $branchStatus ?? 'pending';

        // Default page title
        $title       = $title ?? 'Branch Admin';
        $currentPage = $currentPage ?? '';

        // Pending count for sidebar badges
        $pendingAnnouncements = 0;
        try {
            $stmt = $this->db->prepare(
                "SELECT COUNT(*) AS c FROM `announcements`
                 WHERE `is_active` = 1 AND (`branch_id` IS NULL OR `branch_id` = :bid)
                 AND (`updated_at` > DATE_SUB(NOW(), INTERVAL 7 DAY))"
            );
            $stmt->execute([':bid' => $branchId]);
            $pendingAnnouncements = (int) ($stmt->fetch()['c'] ?? 0);
        } catch (\Throwable $e) { /* ignore */ }

        // Build view path
        $viewFile = self::VIEW_PATH . $view . '.php';
        if (!file_exists($viewFile)) {
            http_response_code(500);
            error_log("Branch Admin view not found: {$viewFile}");
            echo "Error: Branch Admin view '{$view}' could not be found.";
            exit;
        }

        ob_start();
        require $viewFile;
        $content = ob_get_clean();

        $layoutFile = self::VIEW_PATH . 'layout.php';
        if (!file_exists($layoutFile)) {
            echo $content;
            exit;
        }

        require $layoutFile;
        exit;
    }

    /**
     * Render the Branch Admin login view (not wrapped in layout).
     */
    protected function loginView(array $data = []): never
    {
        extract($data, EXTR_SKIP);

        $flash_error   = flash('error');
        $flash_success = flash('success');
        $csrfToken     = generateCsrfToken();
        $title         = $title ?? 'Branch Admin Login';

        $viewFile = self::VIEW_PATH . 'auth/login.php';
        if (!file_exists($viewFile)) {
            http_response_code(500);
            echo 'Error: Branch Admin login view could not be found.';
            exit;
        }

        require $viewFile;
        exit;
    }

    /**
     * Get the current branch ID from session.
     */
    protected function getBranchId(): int
    {
        return (int) ($_SESSION['branch_id'] ?? 0);
    }

    /**
     * Get the current branch code from session.
     */
    protected function getBranchCode(): string
    {
        return (string) ($_SESSION['branch_code'] ?? '');
    }

    /**
     * Handle image upload with validation.
     * (Same logic as AdminController::handleImageUpload but in BranchAdmin namespace.)
     */
    protected function handleImageUpload(string $fieldName, string $subDir = ''): ?string
    {
        if (!isset($_FILES[$fieldName]) || $_FILES[$fieldName]['error'] !== UPLOAD_ERR_OK) {
            return null;
        }

        $file = $_FILES[$fieldName];

        if ($file['size'] > 5 * 1024 * 1024) {
            flash('error', 'Image size must be less than 5MB.');
            return null;
        }

        $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);

        if (!in_array($mimeType, $allowedTypes, true)) {
            flash('error', 'Only JPG, PNG, GIF, and WebP images are allowed.');
            return null;
        }

        $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
        $filename = uniqid('img_', true) . '.' . $extension;
        $uploadDir = PUBLIC_ROOT . '/uploads/' . $subDir;

        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0777, true);
        }
        if (!is_writable($uploadDir)) {
            @chmod($uploadDir, 0777);
            if (!is_writable($uploadDir)) {
                flash('error', 'Upload directory is not writable.');
                return null;
            }
        }

        $destination = $uploadDir . '/' . $filename;
        if (!move_uploaded_file($file['tmp_name'], $destination)) {
            flash('error', 'Failed to upload image.');
            return null;
        }

        @chmod($destination, 0644);
        return 'uploads/' . ($subDir ? $subDir . '/' : '') . $filename;
    }

    /**
     * Delete an image file from the server.
     */
    protected function deleteImageFile(?string $imagePath): bool
    {
        if (empty($imagePath)) {
            return false;
        }

        $bp = defined('BASE_PATH') ? BASE_PATH : '';
        if ($bp !== '' && str_starts_with($imagePath, $bp . '/')) {
            $imagePath = substr($imagePath, strlen($bp) + 1);
        }
        $imagePath = ltrim($imagePath, '/');

        if (!str_starts_with($imagePath, 'uploads/')) {
            if (file_exists(PUBLIC_ROOT . '/uploads/' . $imagePath)) {
                $imagePath = 'uploads/' . $imagePath;
            } else {
                return false;
            }
        }

        $fullPath = PUBLIC_ROOT . '/' . $imagePath;
        if (file_exists($fullPath) && is_file($fullPath)) {
            return unlink($fullPath);
        }
        return false;
    }

    /**
     * Check if a column exists in a database table.
     */
    protected function columnExists(string $table, string $column): bool
    {
        try {
            $stmt = $this->db->prepare(
                "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
                 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table AND COLUMN_NAME = :column"
            );
            $stmt->execute([':table' => $table, ':column' => $column]);
            return (int) $stmt->fetchColumn() > 0;
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Write an entry to the branch_activity_logs table.
     */
    protected function auditLog(string $action, string $entityType = '', ?int $entityId = null, ?string $details = null): void
    {
        $branchId = $this->getBranchId();
        try {
            // Try branch_activity_logs first
            $stmt = $this->db->prepare(
                'INSERT INTO `branch_activity_logs`
                    (`branch_id`, `user_id`, `action`, `model_type`, `model_id`, `details`, `ip_address`, `created_at`)
                 VALUES (:bid, :uid, :action, :etype, :eid, :details, :ip, NOW())'
            );
            $stmt->execute([
                ':bid'      => $branchId,
                ':uid'      => $_SESSION['user_id'] ?? null,
                ':action'   => $action,
                ':etype'    => $entityType ?: null,
                ':eid'      => $entityId,
                ':details'  => $details,
                ':ip'       => $_SERVER['REMOTE_ADDR'] ?? null,
            ]);
        } catch (\Throwable $e) {
            // Fallback to audit_log
            try {
                $stmt = $this->db->prepare(
                    'INSERT INTO `audit_log`
                        (`user_id`, `branch_id`, `action`, `entity_type`, `entity_id`, `details`, `ip_address`, `created_at`)
                     VALUES (:uid, :bid, :action, :etype, :eid, :details, :ip, NOW())'
                );
                $stmt->execute([
                    ':uid'      => $_SESSION['user_id'] ?? null,
                    ':bid'      => $branchId,
                    ':action'   => $action,
                    ':etype'    => $entityType ?: null,
                    ':eid'      => $entityId,
                    ':details'  => $details,
                    ':ip'       => $_SERVER['REMOTE_ADDR'] ?? null,
                ]);
            } catch (\Throwable $e2) {
                error_log('Branch audit log write failed: ' . $e2->getMessage());
            }
        }
    }
}
