<?php
namespace Modules\Users\Controllers;

use Core\Controller;
use Core\Auth;
use Modules\Users\Models\RoleModel;
use UserAuthModel;

class UserController extends Controller {
    private UserAuthModel $userModel;
    private RoleModel $roleModel;

    public function __construct() {
        parent::__construct();
        $this->userModel = new UserAuthModel();
        $this->roleModel = new RoleModel();
    }

    public function index() {
        $this->requirePermission('users', 'view');
        $orgId = Auth::isSuperAdmin() ? null : Auth::orgId();
        $page = max(1, (int)$this->getGet('page', 1));
        $filters = [
            'search' => $this->getGet('search', ''),
            'entity_type' => $this->getGet('entity_type', ''),
            'role_id' => $this->getGet('role', ''),
        ];
        if ($this->getGet('status') === 'active') {
            $filters['is_active'] = 1;
        } elseif ($this->getGet('status') === 'inactive') {
            $filters['is_active'] = 0;
        }

        $result = $this->userModel->getUsersForOrg($orgId, $filters, $page, 20);

        $statsWhere = $orgId !== null ? 'WHERE org_id = ?' : '';
        $statsParams = $orgId !== null ? [$orgId] : [];
        $statsRow = $this->dbQueryOne(
            "SELECT COUNT(*) as total,
                    SUM(IF(is_active = 1, 1, 0)) as active,
                    SUM(IF(entity_type = 'org_user', 1, 0)) as org_users,
                    SUM(IF(entity_type = 'contractor_user', 1, 0)) as contractor_users
             FROM users {$statsWhere}",
            $statsParams
        );

        $roles = $this->roleModel->getAllForOrg($orgId);

        $this->render('users/index', [
            'users' => $result['data'],
            'isPlatformView' => $orgId === null,
            'stats' => [
                'total' => (int)($statsRow['total'] ?? 0),
                'active' => (int)($statsRow['active'] ?? 0),
                'org_users' => (int)($statsRow['org_users'] ?? 0),
                'contractor_users' => (int)($statsRow['contractor_users'] ?? 0),
            ],
            'filters' => array_merge($filters, ['status' => $this->getGet('status', '')]),
            'roles' => $roles,
            'paginationData' => [
                'current_page' => $result['current'],
                'total_pages' => $result['pages'],
                'total_items' => $result['total'],
                'url_pattern' => APP_URL . '/users?' . http_build_query(array_filter(['search' => $filters['search'] ?? ''])) . '&page=',
            ],
        ]);
    }

    public function create() {
        $this->requirePermission('users', 'edit');
        $orgId = Auth::orgId();
        $isSuperAdmin = Auth::isSuperAdmin();

        $this->render('users/create', [
            'contractors' => $orgId
                ? $this->dbQuery("SELECT id, company_name FROM contractors WHERE org_id = ? ORDER BY company_name", [$orgId])
                : $this->dbQuery("SELECT id, company_name FROM contractors ORDER BY company_name"),
            'suppliers' => $orgId
                ? $this->dbQuery("SELECT id, company_name FROM suppliers WHERE org_id = ? ORDER BY company_name", [$orgId])
                : $this->dbQuery("SELECT id, company_name FROM suppliers ORDER BY company_name"),
            'organizations' => $isSuperAdmin ? $this->dbQuery("SELECT id, name FROM organizations ORDER BY name") : [],
            'availableRoles' => $this->roleModel->getAllForOrg($orgId),
            'userRoles' => [],
        ]);
    }

    public function store() {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users/create');
        }

        $entityType = $this->mapEntityType($this->getPost('entity_type', 'org'));
        $entityId = null;
        if ($entityType === 'contractor_user') {
            $entityId = (int)$this->getPost('contractor_id', 0) ?: null;
        } elseif ($entityType === 'supplier_user') {
            $entityId = (int)$this->getPost('supplier_id', 0) ?: null;
        }

        $firstName = trim($this->getPost('first_name', ''));
        $lastName = trim($this->getPost('last_name', ''));
        $email = strtolower(trim($this->getPost('email', '')));

        if (empty($firstName) || empty($lastName) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->setFlash('error', 'A valid first name, last name, and email are required.');
            $this->redirect('/users/create');
        }

        $autoGenerate = !empty($this->getPost('auto_generate_password'));
        $password = $autoGenerate ? substr(bin2hex(random_bytes(6)), 0, 10) : $this->getPost('password', '');
        if (empty($password)) {
            $password = substr(bin2hex(random_bytes(6)), 0, 10);
        }

        $orgId = Auth::isSuperAdmin() ? ((int)$this->getPost('org_id', 0) ?: null) : Auth::orgId();
        if ($orgId === null) {
            $this->setFlash('error', 'Please select which organization this user belongs to.');
            $this->redirect('/users/create');
        }

        $userId = $this->userModel->createUser([
            'org_id' => $orgId,
            'entity_type' => $entityType,
            'entity_id' => $entityId,
            'first_name' => $firstName,
            'last_name' => $lastName,
            'email' => $email,
            'password' => $password,
            'phone' => $this->getPost('phone', ''),
            'is_active' => 1,
        ]);

        foreach ($_POST['roles'] ?? [] as $roleId) {
            $this->userModel->assignRole($userId, (int)$roleId);
        }

        if ($entityType === 'contractor_user' && $entityId) {
            $this->dbExecute("INSERT IGNORE INTO contractor_users (contractor_id, user_id, is_admin, created_at) VALUES (?, ?, 0, NOW())", [$entityId, $userId]);
        } elseif ($entityType === 'supplier_user' && $entityId) {
            $this->dbExecute("INSERT IGNORE INTO supplier_users (supplier_id, user_id, is_admin, created_at) VALUES (?, ?, 0, NOW())", [$entityId, $userId]);
        }

        if (!empty($_POST['send_welcome_email'])) {
            $htmlBody = "<p>Hello {$firstName},</p><p>An account has been created for you on " . APP_NAME . ".</p>" .
                "<p>Email: {$email}<br>" . ($autoGenerate ? "Temporary Password: {$password}" : "Your password was set by your administrator.") . "</p>" .
                "<p><a href=\"" . APP_URL . "/login\">Login here</a></p>";
            sendEmail($email, $firstName . ' ' . $lastName, 'Your ' . APP_NAME . ' account', $htmlBody);
        }

        logAudit('user_created', 'users', 'user', $userId);
        $this->setFlash('success', 'User created successfully.');
        $this->redirect('/users/' . $userId);
    }

    public function show($id) {
        $this->requirePermission('users', 'view');
        $user = $this->getScopedUser((int)$id);
        $roles = $this->userModel->getUserRoles((int)$id);
        $permRows = $this->userModel->getUserPermissions((int)$id);
        $permissions = [];
        foreach ($permRows as $p) {
            $permissions[$p['module']][] = $p['action'];
        }
        $activities = $this->dbQuery(
            "SELECT CONCAT(REPLACE(action, '_', ' ')) as description, created_at FROM audit_logs WHERE user_id = ? ORDER BY created_at DESC LIMIT 15",
            [$id]
        );

        $this->render('users/show', [
            'user' => $user,
            'userRoles' => $roles,
            'permissions' => $permissions,
            'activities' => $activities,
        ]);
    }

    public function edit($id) {
        $this->requirePermission('users', 'edit');
        $user = $this->getScopedUser((int)$id);
        $orgId = $user['org_id'] ?? null;
        $userRoleIds = array_column($this->userModel->getUserRoles((int)$id), 'id');

        $this->render('users/create', [
            'user' => $user,
            'contractors' => $orgId
                ? $this->dbQuery("SELECT id, company_name FROM contractors WHERE org_id = ? ORDER BY company_name", [$orgId])
                : $this->dbQuery("SELECT id, company_name FROM contractors ORDER BY company_name"),
            'suppliers' => $orgId
                ? $this->dbQuery("SELECT id, company_name FROM suppliers WHERE org_id = ? ORDER BY company_name", [$orgId])
                : $this->dbQuery("SELECT id, company_name FROM suppliers ORDER BY company_name"),
            'organizations' => Auth::isSuperAdmin() ? $this->dbQuery("SELECT id, name FROM organizations ORDER BY name") : [],
            'availableRoles' => $this->roleModel->getAllForOrg($orgId),
            'userRoles' => $userRoleIds,
        ]);
    }

    public function update($id) {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users/' . $id . '/edit');
        }

        $user = $this->getScopedUser((int)$id);
        $entityType = $this->mapEntityType($this->getPost('entity_type', $user['entity_type']));
        $entityId = null;
        if ($entityType === 'contractor_user') {
            $entityId = (int)$this->getPost('contractor_id', 0) ?: null;
        } elseif ($entityType === 'supplier_user') {
            $entityId = (int)$this->getPost('supplier_id', 0) ?: null;
        }

        $orgId = $user['org_id'] ?? null;
        if (Auth::isSuperAdmin() && $this->getPost('org_id') !== null) {
            $orgId = (int)$this->getPost('org_id', 0) ?: null;
        }

        $this->userModel->update((int)$id, [
            'first_name' => trim($this->getPost('first_name', $user['first_name'])),
            'last_name' => trim($this->getPost('last_name', $user['last_name'])),
            'email' => strtolower(trim($this->getPost('email', $user['email']))),
            'phone' => $this->getPost('phone', $user['phone']),
            'org_id' => $orgId,
            'entity_type' => $entityType,
            'entity_id' => $entityId,
        ]);

        $existingRoleIds = array_column($this->userModel->getUserRoles((int)$id), 'id');
        $newRoleIds = array_map('intval', $_POST['roles'] ?? []);
        foreach (array_diff($newRoleIds, $existingRoleIds) as $roleId) {
            $this->userModel->assignRole((int)$id, $roleId);
        }
        foreach (array_diff($existingRoleIds, $newRoleIds) as $roleId) {
            $this->userModel->removeRole((int)$id, $roleId);
        }

        logAudit('user_updated', 'users', 'user', (int)$id);
        $this->setFlash('success', 'User updated successfully.');
        $this->redirect('/users/' . $id);
    }

    public function toggleActive($id) {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users/' . $id);
        }
        $user = $this->getScopedUser((int)$id);
        $this->userModel->update((int)$id, ['is_active' => $user['is_active'] ? 0 : 1]);
        logAudit('user_toggle_active', 'users', 'user', (int)$id);
        $this->setFlash('success', 'User status updated.');
        $this->redirect('/users/' . $id);
    }

    public function delete($id) {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users');
        }
        $this->getScopedUser((int)$id);
        $this->userModel->update((int)$id, ['is_active' => 0]);
        logAudit('user_deactivated', 'users', 'user', (int)$id);
        $this->setFlash('success', 'User deactivated.');
        $this->redirect('/users');
    }

    public function resetPassword($id) {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users/' . $id);
        }
        $user = $this->getScopedUser((int)$id);

        $token = generateToken(48);
        $this->dbExecute(
            "INSERT INTO password_reset_tokens (email, token, expires_at, created_at) VALUES (?, ?, ?, NOW())",
            [$user['email'], $token, date('Y-m-d H:i:s', strtotime('+1 hour'))]
        );
        $resetUrl = APP_URL . '/reset-password/' . $token;
        $htmlBody = "<p>Hello " . htmlspecialchars($user['first_name']) . ",</p><p>An administrator initiated a password reset for your account.</p><p><a href=\"{$resetUrl}\">Reset your password</a> (expires in 1 hour).</p>";
        sendEmail($user['email'], trim($user['first_name'] . ' ' . $user['last_name']), 'Password Reset Requested', $htmlBody);

        logAudit('user_reset_password_sent', 'users', 'user', (int)$id);
        $this->setFlash('success', 'Password reset email sent.');
        $this->redirect('/users/' . $id);
    }

    public function disableOtp($id) {
        $this->requirePermission('users', 'edit');
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/users/' . $id);
        }
        $this->getScopedUser((int)$id);
        $this->userModel->disableOtp((int)$id);
        logAudit('user_otp_disabled', 'users', 'user', (int)$id);
        $this->setFlash('success', 'Two-factor authentication disabled for this user.');
        $this->redirect('/users/' . $id);
    }

    public function updateProfile() {
        $this->requireAuth();
        $userId = Auth::id();

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            if (!$this->validateCsrf()) {
                $this->setFlash('error', 'Invalid security token.');
                $this->redirect('/profile');
            }

            if ($this->getPost('locale') !== null || $this->getPost('timezone') !== null) {
                $this->userModel->update($userId, [
                    'locale' => $this->getPost('locale'),
                    'timezone' => $this->getPost('timezone'),
                    'currency' => $this->getPost('currency'),
                ]);
                $this->setFlash('success', 'Preferences updated successfully.');
            } else {
                $data = [
                    'first_name' => trim($this->getPost('first_name', '')),
                    'last_name' => trim($this->getPost('last_name', '')),
                    'phone' => $this->getPost('phone', ''),
                ];
                if (!empty($_FILES['avatar']['name'])) {
                    $upload = $this->uploadFile($_FILES['avatar'], 'avatars', ['jpg', 'jpeg', 'png', 'webp']);
                    if ($upload) {
                        $data['avatar'] = $upload['path'];
                    }
                }
                $this->userModel->update($userId, $data);
                $this->setFlash('success', 'Profile information updated successfully.');
            }
            $this->redirect('/profile');
        }

        $user = $this->getProfileUser($userId);
        $this->render('users/profile', ['user' => $user]);
    }

    public function changePassword() {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect('/profile');
        }

        $userId = Auth::id();
        $user = $this->userModel->find($userId);
        $current = $this->getPost('current_password', '');
        $new = $this->getPost('new_password', '');
        $confirm = $this->getPost('confirm_password', '');

        if (!password_verify($current, $user['password'])) {
            $this->setFlash('error', 'Current password is incorrect.');
            $this->redirect('/profile');
        }
        if (strlen($new) < 8 || $new !== $confirm) {
            $this->setFlash('error', 'New password must be at least 8 characters and match confirmation.');
            $this->redirect('/profile');
        }

        $this->userModel->update($userId, ['password' => password_hash($new, PASSWORD_BCRYPT)]);
        logAudit('password_changed', 'users', 'user', $userId);
        $this->setFlash('success', 'Password updated successfully.');
        $this->redirect('/profile');
    }

    private function getProfileUser(int $userId): array {
        $user = $this->userModel->find($userId);
        $otp = $this->userModel->getOtpSecret($userId);
        $user['2fa_enabled'] = !empty($otp['is_enabled']);
        return $user;
    }

    private function getScopedUser(int $id): array {
        $user = $this->userModel->find($id);
        $sameOrg = $user && (int)($user['org_id'] ?? 0) === (int)Auth::orgId();
        if (!$user || (!Auth::isSuperAdmin() && !$sameOrg)) {
            $this->setFlash('error', 'User not found.');
            $this->redirect('/users');
        }
        $otp = $this->userModel->getOtpSecret($id);
        $user['2fa_enabled'] = !empty($otp['is_enabled']);
        return $user;
    }

    private function mapEntityType(string $value): string {
        return match ($value) {
            'contractor', 'contractor_user' => 'contractor_user',
            'supplier', 'supplier_user' => 'supplier_user',
            'super_admin' => 'super_admin',
            default => 'org_user',
        };
    }
}
