<?php
declare(strict_types=1);

function auth_permission_labels(): array
{
    return array(
        'dashboard' => 'Dashboard',
        'booking' => 'Booking',
        'booking_deposit_admin' => 'Booking deposit @Admin',
        'booking_deposit_branch' => 'Booking deposit @Branch',
        'booking_deposit_admin_edit' => 'Booking deposit @Admin edit',
        'booking_deposit_branch_edit' => 'Booking deposit @Branch edit',
        'quick_service' => 'Quick Service',
        'quick_service_payment' => 'Quick Service Payment',
        'queue' => 'Queue / Jobs',
        'queue_payment' => 'Queue Payment',
        'queue_start_waiting' => 'Queue start from Waiting',
        'queue_cancel_waiting' => 'Cancel queue in Waiting',
        'queue_bill_control' => 'Queue status reverse / reopen bills',
        'queue_cancel_closed' => 'Cancel closed bills',
        'queue_cancel_open' => 'Cancel open bills',
        'queue_edit_open' => 'Edit open queue data',
        'bill_management' => 'Bill Management',
        'services' => 'Services',
        'branches_manage' => 'Branch Management',
        'member' => 'Member',
        'member_manage' => 'Member list / management',
        'member_edit' => 'Member edit',
        'member_delete' => 'Member delete',
        'audit_report' => 'Audit Report',
        'report_service' => 'Service Report',
        'sale_report' => 'Sale Report',
        'void_report' => 'Void Report',
        'loyalty' => 'Loyalty',
        'employees' => 'Employees and access',
        'maintenance_clear_log' => 'Maintenance: Clear Log',
        'maintenance_clear_booking_queue' => 'Maintenance: Clear Booking / Queue',
        'maintenance_clear_sale' => 'Maintenance: Clear Sale',
        'maintenance_reset_operational_data' => 'Maintenance: Reset Operational Data',
        'maintenance_cleanup_quick_service_guest' => 'Maintenance: Cleanup Quick Service Guest Vehicle Data',
        'maintenance_prepare_deployment_package' => 'Maintenance: Prepare Deployment Package',
        'maintenance_compare_database_structure' => 'Maintenance: Compare Database Structure',
        'maintenance_apply_database_structure' => 'Maintenance: Apply Database Structure',
        'maintenance_backup_database' => 'Maintenance: Backup Database',
        'maintenance_restore_database' => 'Maintenance: Restore Database',
    );
}

function auth_position_seed_data(): array
{
    $allPermissions = array_keys(auth_permission_labels());

    return array(
        array('Administrator', 'admin', 'all', $allPermissions),
        array('Branch Manager', 'manager', 'assigned', array('dashboard', 'booking', 'quick_service', 'quick_service_payment', 'queue', 'queue_payment', 'queue_start_waiting', 'queue_cancel_waiting', 'bill_management', 'services', 'branches_manage', 'member', 'member_manage', 'member_edit', 'member_delete', 'audit_report', 'report_service', 'sale_report', 'void_report', 'loyalty')),
        array('Service Advisor', 'advisor', 'assigned', array('dashboard', 'booking', 'quick_service', 'quick_service_payment', 'queue', 'queue_payment', 'queue_start_waiting', 'queue_cancel_waiting', 'member', 'loyalty')),
        array('Accounting', 'accounting', 'assigned', array('dashboard', 'bill_management', 'audit_report', 'report_service', 'sale_report', 'void_report')),
        array('Viewer', 'viewer', 'assigned', array('dashboard', 'report_service', 'sale_report', 'void_report')),
    );
}

function ensure_auth_schema(mysqli $conn): void
{
    static $checked = false;

    if ($checked) {
        return;
    }

    $schemaCurrent = runtime_schema_bootstrap_is_current();
    $checked = true;

    if (!$schemaCurrent) {
        $conn->query("
            CREATE TABLE IF NOT EXISTS positions (
                position_id INT AUTO_INCREMENT PRIMARY KEY,
                position_name VARCHAR(80) NOT NULL,
                role_key VARCHAR(40) NOT NULL,
                branch_scope ENUM('all', 'assigned') NOT NULL DEFAULT 'assigned',
                permissions TEXT NULL,
                is_active TINYINT(1) NOT NULL DEFAULT 1,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                UNIQUE KEY uniq_positions_role_key (role_key)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
        ");

        $employeeColumns = array(
            'position_id' => "ALTER TABLE employees ADD COLUMN position_id INT NULL AFTER employee_id",
            'username' => "ALTER TABLE employees ADD COLUMN username VARCHAR(80) NULL AFTER phone",
            'password_hash' => "ALTER TABLE employees ADD COLUMN password_hash VARCHAR(255) NULL AFTER username",
            'branch_scope' => "ALTER TABLE employees ADD COLUMN branch_scope ENUM('all', 'assigned') NOT NULL DEFAULT 'assigned' AFTER password_hash",
            'default_start_page' => "ALTER TABLE employees ADD COLUMN default_start_page VARCHAR(40) NULL AFTER branch_scope",
            'last_login_at' => "ALTER TABLE employees ADD COLUMN last_login_at DATETIME NULL AFTER default_start_page",
        );

        foreach ($employeeColumns as $columnName => $alterSql) {
            $columnResult = $conn->query("SHOW COLUMNS FROM employees LIKE '" . $conn->real_escape_string($columnName) . "'");

            if (!($columnResult instanceof mysqli_result) || $columnResult->num_rows === 0) {
                $conn->query($alterSql);
            }
        }

        $indexResult = $conn->query("SHOW INDEX FROM employees WHERE Key_name = 'uniq_employees_username'");
        if (!($indexResult instanceof mysqli_result) || $indexResult->num_rows === 0) {
            $conn->query("ALTER TABLE employees ADD UNIQUE KEY uniq_employees_username (username)");
        }

        $positionIndexResult = $conn->query("SHOW INDEX FROM employees WHERE Key_name = 'idx_employees_position_id'");
        if (!($positionIndexResult instanceof mysqli_result) || $positionIndexResult->num_rows === 0) {
            $conn->query("ALTER TABLE employees ADD INDEX idx_employees_position_id (position_id)");
        }

        $conn->query("
            CREATE TABLE IF NOT EXISTS employee_branch_access (
                employee_id INT NOT NULL,
                branch_name VARCHAR(120) NOT NULL,
                PRIMARY KEY (employee_id, branch_name),
                INDEX idx_employee_branch_name (branch_name),
                CONSTRAINT fk_employee_branch_access_employee
                    FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
                    ON UPDATE CASCADE ON DELETE CASCADE
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
        ");
    }

    foreach (auth_position_seed_data() as $positionSeed) {
        $positionName = $positionSeed[0];
        $roleKey = $positionSeed[1];
        $branchScope = $positionSeed[2];
        $permissions = json_encode($positionSeed[3], JSON_UNESCAPED_UNICODE);

        $stmt = db_prepare($conn, "
            INSERT INTO positions (position_name, role_key, branch_scope, permissions)
            VALUES (?, ?, ?, ?)
            ON DUPLICATE KEY UPDATE
                role_key = role_key
        ");
        $stmt->bind_param('ssss', $positionName, $roleKey, $branchScope, $permissions);
        $stmt->execute();
        $stmt->close();
    }

    $adminPermissions = json_encode(array_keys(auth_permission_labels()), JSON_UNESCAPED_UNICODE);
    $adminPermissionStmt = db_prepare($conn, "
        UPDATE positions
        SET permissions = ?
        WHERE role_key = 'admin'
    ");
    $adminPermissionStmt->bind_param('s', $adminPermissions);
    $adminPermissionStmt->execute();
    $adminPermissionStmt->close();

    $adminPositionId = auth_position_id_by_role($conn, 'admin');
    if ($adminPositionId > 0) {
        $adminResult = $conn->query("SELECT employee_id FROM employees WHERE username = 'admin' LIMIT 1");

        if (!($adminResult instanceof mysqli_result) || $adminResult->num_rows === 0) {
            $passwordHash = password_hash('admin123', PASSWORD_DEFAULT);
            $stmt = db_prepare($conn, "
                INSERT INTO employees (position_id, name, position, phone, username, password_hash, branch_scope, is_active)
                VALUES (?, 'System Administrator', 'Administrator', '', 'admin', ?, 'all', 1)
            ");
            $stmt->bind_param('is', $adminPositionId, $passwordHash);
            $stmt->execute();
            $stmt->close();
        }
    }

    $advisorPositionId = auth_position_id_by_role($conn, 'advisor');
    if ($advisorPositionId > 0) {
        $conn->query("
            UPDATE employees
            SET position_id = {$advisorPositionId}
            WHERE position_id IS NULL
                AND (position IS NULL OR position = '' OR position = 'Advisor')
        ");
    }
}

function auth_position_id_by_role(mysqli $conn, string $roleKey): int
{
    $stmt = db_prepare($conn, "
        SELECT position_id
        FROM positions
        WHERE role_key = ?
        LIMIT 1
    ");
    $stmt->bind_param('s', $roleKey);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    return (int) ($row['position_id'] ?? 0);
}

function auth_current_user(mysqli $conn): ?array
{
    static $cachedUser = null;
    static $loaded = false;

    if ($loaded) {
        return $cachedUser;
    }

    $loaded = true;
    $employeeId = (int) ($_SESSION['employee_id'] ?? 0);

    if ($employeeId <= 0) {
        return null;
    }

    $stmt = db_prepare($conn, "
        SELECT
            e.employee_id,
            e.name,
            e.position,
            e.position_id,
            e.username,
            e.branch_scope,
            e.default_start_page,
            e.is_active,
            p.position_name,
            p.role_key,
            p.permissions,
            p.branch_scope AS position_branch_scope
        FROM employees e
        LEFT JOIN positions p ON p.position_id = e.position_id
        WHERE e.employee_id = ?
        LIMIT 1
    ");
    $stmt->bind_param('i', $employeeId);
    $stmt->execute();
    $user = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    if (!$user || (int) ($user['is_active'] ?? 0) !== 1) {
        unset($_SESSION['employee_id']);
        return null;
    }

    $cachedUser = $user;

    return $cachedUser;
}

function auth_page_path(string $page): string
{
    $scriptName = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? ''));

    if (strpos($scriptName, '/process/') !== false) {
        return '../pages/' . $page;
    }

    return $page;
}

function is_logged_in(mysqli $conn): bool
{
    return auth_current_user($conn) !== null;
}

function require_login(mysqli $conn): void
{
    if (is_logged_in($conn)) {
        return;
    }

    $_SESSION['intended_url'] = (string) ($_SERVER['REQUEST_URI'] ?? '');
    redirect_with_flash(auth_page_path('login.php'), 'error', 'Please login to use the system.');
}

function auth_user_permissions(?array $user): array
{
    if (!$user) {
        return array();
    }

    // Administrator role is intended to keep full access, including newly added permissions.
    if ((string) ($user['role_key'] ?? '') === 'admin') {
        return array_keys(auth_permission_labels());
    }

    $permissions = json_decode((string) ($user['permissions'] ?? '[]'), true);

    return is_array($permissions) ? array_values(array_filter(array_map('strval', $permissions))) : array();
}

function can_access(mysqli $conn, string $permission): bool
{
    $user = auth_current_user($conn);

    if (!$user) {
        return false;
    }

    return in_array($permission, auth_user_permissions($user), true);
}

function auth_permissions_allow_member_area(array $permissions): bool
{
    return in_array('member', $permissions, true)
        || in_array('member_manage', $permissions, true);
}

function can_access_member_area(mysqli $conn): bool
{
    return auth_permissions_allow_member_area(auth_user_permissions(auth_current_user($conn)));
}

function can_access_open_bill(mysqli $conn): bool
{
    return can_access($conn, 'booking')
        || can_access($conn, 'quick_service')
        || can_access_member_area($conn);
}

function can_access_manage_system(mysqli $conn): bool
{
    return can_access($conn, 'services')
        || can_access($conn, 'branches_manage')
        || can_access($conn, 'employees')
        || can_access($conn, 'bill_management')
        || can_access_data_maintenance($conn);
}

function auth_start_page_definitions(): array
{
    return array(
        'dashboard' => array(
            'page' => 'dashboard.php',
            'label' => 'Dashboard',
            'permission' => 'dashboard',
        ),
        'booking' => array(
            'page' => 'booking.php',
            'label' => 'New booking',
            'permission' => 'booking',
        ),
        'quick_service' => array(
            'page' => 'quick_service.php',
            'label' => 'Quick Service',
            'permission' => 'quick_service',
        ),
        'queue' => array(
            'page' => 'queue.php',
            'label' => 'Queue',
            'permission' => 'queue',
        ),
        'member' => array(
            'page' => 'member.php',
            'label' => 'Member',
            'permission' => null,
        ),
        'loyalty' => array(
            'page' => 'loyalty.php',
            'label' => 'Loyalty',
            'permission' => 'loyalty',
        ),
        'sale_report' => array(
            'page' => 'sale_report.php',
            'label' => 'Sale Report',
            'permission' => 'sale_report',
        ),
        'report_service' => array(
            'page' => 'report_service.php',
            'label' => 'Service Report',
            'permission' => 'report_service',
        ),
        'void_report' => array(
            'page' => 'void_report.php',
            'label' => 'Void Report',
            'permission' => 'void_report',
        ),
        'audit_report' => array(
            'page' => 'audit_report.php',
            'label' => 'Audit Report',
            'permission' => 'audit_report',
        ),
        'bill_management' => array(
            'page' => 'bill_management.php',
            'label' => 'Bill Management',
            'permission' => 'bill_management',
        ),
        'manage_system' => array(
            'page' => 'manage_system.php',
            'label' => 'Manage System',
            'permission' => null,
        ),
    );
}

function auth_default_start_page_options(): array
{
    $options = array(
        '' => 'Auto by permissions',
    );

    foreach (auth_start_page_definitions() as $startPageKey => $definition) {
        $options[$startPageKey] = (string) ($definition['label'] ?? $startPageKey);
    }

    return $options;
}

function normalize_auth_default_start_page(string $startPageKey): string
{
    $startPageKey = trim($startPageKey);

    if ($startPageKey === '' || $startPageKey === 'auto') {
        return '';
    }

    return array_key_exists($startPageKey, auth_start_page_definitions()) ? $startPageKey : '';
}

function auth_default_start_page_label(string $startPageKey): string
{
    $startPageKey = normalize_auth_default_start_page($startPageKey);
    $options = auth_default_start_page_options();

    return (string) ($options[$startPageKey] ?? $options['']);
}

function auth_permissions_allow_data_maintenance_access(array $permissions): bool
{
    foreach (array(
        'maintenance_clear_log',
        'maintenance_clear_booking_queue',
        'maintenance_clear_sale',
        'maintenance_reset_operational_data',
        'maintenance_cleanup_quick_service_guest',
        'maintenance_prepare_deployment_package',
        'maintenance_compare_database_structure',
        'maintenance_apply_database_structure',
        'maintenance_backup_database',
        'maintenance_restore_database',
    ) as $permissionKey) {
        if (in_array($permissionKey, $permissions, true)) {
            return true;
        }
    }

    return false;
}

function auth_permissions_allow_start_page(array $permissions, string $startPageKey): bool
{
    $startPageKey = normalize_auth_default_start_page($startPageKey);

    if ($startPageKey === '') {
        return false;
    }

    if ($startPageKey === 'manage_system') {
        return in_array('services', $permissions, true)
            || in_array('branches_manage', $permissions, true)
            || in_array('employees', $permissions, true)
            || in_array('bill_management', $permissions, true)
            || auth_permissions_allow_data_maintenance_access($permissions);
    }

    if ($startPageKey === 'member') {
        return auth_permissions_allow_member_area($permissions);
    }

    $definitions = auth_start_page_definitions();
    $permissionKey = (string) ($definitions[$startPageKey]['permission'] ?? '');

    return $permissionKey !== '' && in_array($permissionKey, $permissions, true);
}

function auth_default_start_page_path(string $startPageKey): string
{
    $startPageKey = normalize_auth_default_start_page($startPageKey);
    $definitions = auth_start_page_definitions();

    return (string) ($definitions[$startPageKey]['page'] ?? 'dashboard.php');
}

function auth_preferred_landing_page(mysqli $conn): string
{
    $user = auth_current_user($conn);
    $permissions = auth_user_permissions($user);
    $preferredStartPage = normalize_auth_default_start_page((string) ($user['default_start_page'] ?? ''));

    if ($preferredStartPage !== '' && auth_permissions_allow_start_page($permissions, $preferredStartPage)) {
        return auth_default_start_page_path($preferredStartPage);
    }

    foreach (array_keys(auth_start_page_definitions()) as $startPageKey) {
        if (auth_permissions_allow_start_page($permissions, $startPageKey)) {
            return auth_default_start_page_path($startPageKey);
        }
    }

    return 'dashboard.php';
}

function require_permission(mysqli $conn, string $permission): void
{
    require_login($conn);

    if (can_access($conn, $permission)) {
        return;
    }

    redirect_with_flash(auth_page_path(auth_preferred_landing_page($conn)), 'error', 'You do not have permission to access that function.');
}

function auth_scope(mysqli $conn): string
{
    $user = auth_current_user($conn);

    if (!$user) {
        return 'assigned';
    }

    $scope = (string) ($user['branch_scope'] ?? '');
    if ($scope === '') {
        $scope = (string) ($user['position_branch_scope'] ?? 'assigned');
    }

    return $scope === 'all' ? 'all' : 'assigned';
}

function auth_assigned_branches(mysqli $conn): array
{
    $user = auth_current_user($conn);

    if (!$user) {
        return array();
    }

    $stmt = db_prepare($conn, "
        SELECT branch_name
        FROM employee_branch_access
        WHERE employee_id = ?
        ORDER BY branch_name ASC
    ");
    $employeeId = (int) $user['employee_id'];
    $stmt->bind_param('i', $employeeId);
    $stmt->execute();
    $result = $stmt->get_result();
    $branches = array();

    while ($row = $result->fetch_assoc()) {
        $branchName = trim((string) ($row['branch_name'] ?? ''));

        if ($branchName !== '') {
            $branches[] = normalize_branch_name($branchName);
        }
    }

    $stmt->close();

    return $branches;
}

function auth_filter_branch_options(mysqli $conn, array $branchNames): array
{
    $branchNames = array_values(array_unique(array_filter(array_map('normalize_branch_name', array_map('strval', $branchNames)))));

    if (auth_scope($conn) === 'all') {
        return $branchNames;
    }

    $allowedBranches = auth_assigned_branches($conn);

    return array_values(array_intersect($branchNames, $allowedBranches));
}

function auth_can_access_branch(mysqli $conn, ?string $branchName): bool
{
    if (auth_scope($conn) === 'all') {
        return true;
    }

    $branchName = normalize_branch_name($branchName);

    if ($branchName === '') {
        return false;
    }

    return in_array($branchName, auth_assigned_branches($conn), true);
}

function auth_branch_condition(mysqli $conn, string $alias, string &$types, array &$params): string
{
    if (auth_scope($conn) === 'all') {
        return '';
    }

    $branches = auth_assigned_branches($conn);

    if (empty($branches)) {
        return '1 = 0';
    }

    $placeholders = implode(',', array_fill(0, count($branches), '?'));

    foreach ($branches as $branchName) {
        $types .= 's';
        $params[] = $branchName;
    }

    return "{$alias}.branch_name IN ({$placeholders})";
}
