<?php
require_once 'Logger.php';

class User {
    private $db;
    private $logger;
    private $hasTerminalColumn = false;
    private const ROLE_LABELS = [
        'admin' => 'Admin',
        'super_admin' => 'Super Admin',
        'controller' => 'Controller',
        'warehouse' => 'Warehouse',
        'branch' => 'Branch',
        'branch_manager' => 'Branch Manager',
        'sales' => 'Sales',
        'sales_assistant' => 'Sales Assistant',
    ];

    public function __construct($db, $logger = null) {
        $this->db = $db;
        $this->logger = $logger;
        $this->ensureRoleEnum();
        $this->ensureTerminalColumn();
    }

    private function ensureRoleEnum() {
        try {
            $this->db->exec("ALTER TABLE users MODIFY role ENUM('warehouse','branch','branch_manager','sales','sales_assistant','admin','super_admin','controller') NOT NULL");
        } catch (Exception $e) {
            error_log("Unable to ensure users.role supports controller: " . $e->getMessage());
        }
    }

    public static function getRoleOptions() {
        return self::ROLE_LABELS;
    }

    public static function getRoleLabel($role) {
        return self::ROLE_LABELS[$role] ?? ucwords(str_replace('_', ' ', (string)$role));
    }

    public static function roleRequiresBranch($role) {
        return in_array($role, ['warehouse', 'branch', 'branch_manager', 'sales', 'sales_assistant'], true);
    }

    public static function roleRequiresTerminal($role) {
        return in_array($role, ['warehouse', 'branch', 'branch_manager', 'sales', 'sales_assistant'], true);
    }

    // Admin/super_admin are system-wide roles that never *require* a branch or
    // terminal, but may optionally be assigned one so they can complete MRA EIS
    // registration/testing flows that key off the acting user's branch (e.g.
    // create_sale.php, terminalForUserAndBranch()) - without becoming a
    // branch-restricted account. roleRequiresBranch/roleRequiresTerminal stay
    // untouched since those still drive validation requirements elsewhere.
    public static function roleAllowsBranch($role) {
        return self::roleRequiresBranch($role) || in_array($role, ['admin', 'super_admin'], true);
    }

    public static function roleAllowsTerminal($role) {
        return self::roleRequiresTerminal($role) || in_array($role, ['admin', 'super_admin'], true);
    }

    private function ensureTerminalColumn() {
        try {
            $stmt = $this->db->query("SHOW COLUMNS FROM users LIKE 'terminal_id'");
            $this->hasTerminalColumn = (bool)$stmt->fetch(PDO::FETCH_ASSOC);

            if (!$this->hasTerminalColumn) {
                $this->db->exec("ALTER TABLE users ADD COLUMN terminal_id INT(11) DEFAULT NULL AFTER branch_id");
                $this->hasTerminalColumn = true;
            }

            try {
                $this->db->exec("ALTER TABLE users ADD KEY idx_users_terminal_id (terminal_id)");
            } catch (Exception $e) {
                // The index may already exist; the column is the part required for assignment.
            }
        } catch (Exception $e) {
            error_log("Unable to ensure users.terminal_id column: " . $e->getMessage());
            $this->hasTerminalColumn = false;
        }
    }

    private function normalizeTerminalId($terminal_id) {
        if ($terminal_id === null || $terminal_id === '') {
            return null;
        }

        return (int)$terminal_id;
    }

    private function validateTerminalAssignment($branch_id, $terminal_id, $required = false) {
        if (!$this->hasTerminalColumn) {
            return null;
        }

        $terminal_id = $this->normalizeTerminalId($terminal_id);
        if (!$terminal_id) {
            if ($required) {
                throw new InvalidArgumentException('Please select a terminal for this user.');
            }

            return null;
        }

        $stmt = $this->db->prepare("
            SELECT COUNT(*)
            FROM mra_eis_terminals
            WHERE id = :terminal_id
              AND branch_id = :branch_id
        ");
        $stmt->bindValue(':terminal_id', $terminal_id, PDO::PARAM_INT);
        $stmt->bindValue(':branch_id', (int)$branch_id, PDO::PARAM_INT);
        $stmt->execute();

        if ((int)$stmt->fetchColumn() === 0) {
            throw new InvalidArgumentException('Selected terminal does not belong to the selected branch.');
        }

        return $terminal_id;
    }

    public function getTerminalsByBranch($branch_id) {
        $stmt = $this->db->prepare("
            SELECT id, terminal_label, status, branch_id
            FROM mra_eis_terminals
            WHERE branch_id = :branch_id
            ORDER BY terminal_label, id
        ");
        $stmt->bindValue(':branch_id', (int)$branch_id, PDO::PARAM_INT);
        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getAllTerminals() {
        $stmt = $this->db->prepare("
            SELECT id, terminal_label, status, branch_id
            FROM mra_eis_terminals
            ORDER BY terminal_label, id
        ");
        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function register($username, $password, $role, $email, $branch_id, $terminal_id = null, $requireTerminal = false) {
        if (!array_key_exists($role, self::ROLE_LABELS)) {
            throw new InvalidArgumentException('Invalid user role selected.');
        }
        $branch_id = self::roleAllowsBranch($role) ? $branch_id : null;
        if (self::roleRequiresBranch($role) && ($branch_id === null || $branch_id === '')) {
            throw new InvalidArgumentException('Please select a branch for this user.');
        }
        if (!self::roleAllowsTerminal($role)) {
            $terminal_id = null;
            $requireTerminal = false;
        }
        $terminal_id = $this->validateTerminalAssignment($branch_id, $terminal_id, $requireTerminal);
        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
        $sql = "INSERT INTO users (username, password, role, email, branch_id, terminal_id) VALUES (:username, :password, :role, :email, :branch_id, :terminal_id)";
        $stmt = $this->db->prepare($sql);
        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':password', $hashedPassword);
        $stmt->bindParam(':role', $role);
        $stmt->bindParam(':email', $email);
        $stmt->bindParam(':branch_id', $branch_id);
        $stmt->bindParam(':terminal_id', $terminal_id);

        $result = $stmt->execute();

        if ($result && $this->logger) {
            $userId = $this->db->lastInsertId();
            $this->logger->logUser('CREATE', $userId, [
                'username' => $username,
                'role' => $role,
                'email' => $email,
                'branch_id' => $branch_id,
                'terminal_id' => $terminal_id
            ]);
        }

        return $result;
    }

    public function login($username, $password) {
        $sql = "SELECT * FROM users WHERE username = :username";
        $stmt = $this->db->prepare($sql);
        $stmt->bindParam(':username', $username);
        $stmt->execute();
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($user) {
            if (password_verify($password, $user['password'])) {
                return $user;
            } else {
                error_log("Password verification failed for username: $username");
            }
        } else {
            error_log("User not found: $username");
        }
        return false;
    }

    public function getUserById($id) {
        $sql = "SELECT * FROM users WHERE user_id = :id";
        $stmt = $this->db->prepare($sql);
        $stmt->bindParam(':id', $id);
        $stmt->execute();
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    public function updateUser($id, $username, $password, $role, $email, $branch_id, $terminal_id = null, $requireTerminal = false) {
        if (!array_key_exists($role, self::ROLE_LABELS)) {
            throw new InvalidArgumentException('Invalid user role selected.');
        }
        $branch_id = self::roleAllowsBranch($role) ? $branch_id : null;
        if (self::roleRequiresBranch($role) && ($branch_id === null || $branch_id === '')) {
            throw new InvalidArgumentException('Please select a branch for this user.');
        }
        if (!self::roleAllowsTerminal($role)) {
            $terminal_id = null;
            $requireTerminal = false;
        }
        $terminal_id = $this->validateTerminalAssignment($branch_id, $terminal_id, $requireTerminal);
        // Get old values for logging
        $oldUser = null;
        if ($this->logger) {
            $oldUser = $this->getUserById($id);
        }

        $query = "UPDATE users SET username = :username, role = :role, email = :email, branch_id = :branch_id, terminal_id = :terminal_id";

        if (!empty($password)) {
            $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
            $query .= ", password = :password";
        }

        $query .= " WHERE user_id = :id";

        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':role', $role);
        $stmt->bindParam(':email', $email);
        $stmt->bindParam(':branch_id', $branch_id);
        $stmt->bindParam(':terminal_id', $terminal_id);
        $stmt->bindParam(':id', $id);

        if (!empty($password)) {
            $stmt->bindParam(':password', $hashedPassword);
        }

        $result = $stmt->execute();

        if ($result && $this->logger && $oldUser) {
            $newValues = [
                'username' => $username,
                'role' => $role,
                'email' => $email,
                'branch_id' => $branch_id,
                'terminal_id' => $terminal_id
            ];
            if (!empty($password)) {
                $newValues['password'] = '***CHANGED***';
            }

            $this->logger->logUser('UPDATE', $id, $newValues, $oldUser);
        }

        return $result;
    }

    public function deleteUser($id) {
        // Get user info for logging before deletion
        $userInfo = null;
        if ($this->logger) {
            $userInfo = $this->getUserById($id);
        }

        $query = "DELETE FROM users WHERE user_id = :id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $id);
        $result = $stmt->execute();

        if ($result && $this->logger && $userInfo) {
            $this->logger->logUser('DELETE', $id, null, $userInfo);
        }

        return $result;
    }

    public function getAllUsers() {
        $query = "SELECT u.user_id, u.username, u.email, u.role, b.branch_name,
                         met.terminal_label
                 FROM users u 
                 LEFT JOIN branches b ON u.branch_id = b.branch_id
                 LEFT JOIN mra_eis_terminals met ON u.terminal_id = met.id";
        $stmt = $this->db->prepare($query);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getUserBranch($userId) {
        $query = "SELECT branch_id FROM users WHERE user_id = :user_id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':user_id', $userId);
        $stmt->execute();
        return $stmt->fetchColumn();
    }

    public function isUserInBranch($userId, $branchId) {
        $query = "SELECT COUNT(*) FROM users WHERE user_id = :user_id AND branch_id = :branch_id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':user_id', $userId);
        $stmt->bindParam(':branch_id', $branchId);
        $stmt->execute();
        return $stmt->fetchColumn() > 0;
    }
}
?>
