<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\Validator;
use App\Models\AuditLog;
use App\Models\Role;
use App\Models\User;

final class UserController extends BaseController
{
    public function index(Request $request): void
    {
        $this->requireModule('users');
        $status = (string) ($_GET['status'] ?? 'active');
        if (!in_array($status, ['active', 'stopped'], true)) {
            $status = 'active';
        }
        $roles = Role::all();
        $roleCodes = [];
        foreach ($roles as $role) {
            $roleCodes[] = (string) $role['code'];
        }
        $role = (string) ($_GET['role'] ?? '');
        if (!in_array($role, $roleCodes, true)) {
            $role = '';
        }
        $q = trim((string) ($_GET['q'] ?? ''));
        $all = User::listForTenant((int) Auth::instance()->tenantId(), false);
        $counts = ['all' => count($all), 'active' => 0, 'stopped' => 0];
        $roleCounts = [];
        foreach ($roleCodes as $code) {
            $roleCounts[$code] = 0;
        }
        foreach ($all as $row) {
            $on = (($row['login_status'] ?? $row['status'] ?? '') === 'active');
            if ($on) {
                $counts['active']++;
            } else {
                $counts['stopped']++;
            }
            foreach (explode(',', (string) ($row['role_codes'] ?? '')) as $code) {
                $code = trim($code);
                if ($code !== '' && isset($roleCounts[$code])) {
                    $roleCounts[$code]++;
                }
            }
        }
        $needle = strtolower($q);
        $rows = array_values(array_filter($all, static function (array $row) use ($status, $role, $needle): bool {
            $on = (($row['login_status'] ?? $row['status'] ?? '') === 'active');
            if ($status === 'active' && !$on) {
                return false;
            }
            if ($status === 'stopped' && $on) {
                return false;
            }
            if ($role !== '') {
                $codes = array_map('trim', explode(',', (string) ($row['role_codes'] ?? '')));
                if (!in_array($role, $codes, true)) {
                    return false;
                }
            }
            if ($needle !== '') {
                $hay = strtolower(trim(($row['first_name'] ?? '') . ' ' . ($row['last_name'] ?? '') . ' ' . ($row['email'] ?? '')));
                if (!str_contains($hay, $needle)) {
                    return false;
                }
            }
            return true;
        }));
        view('users/index', [
            'title' => 'Users',
            'rows' => $rows,
            'roles' => $roles,
            'status' => $status,
            'role' => $role,
            'q' => $q,
            'counts' => $counts,
            'roleCounts' => $roleCounts,
            'notifications' => $this->notifications(),
        ]);
    }

    public function store(Request $request): void
    {
        $this->requireModule('users');
        $this->verifyCsrf($request);
        $data = $request->all();
        $v = (new Validator())
            ->required($data, ['first_name' => 'First name', 'last_name' => 'Last name', 'email' => 'Email', 'password' => 'Password'])
            ->email($data, 'email', 'Email', false);
        if (strlen((string) ($data['password'] ?? '')) < (int) config('security.password_min_length', 8)) {
            $v->add('password', 'Password must be at least 8 characters.');
        }
        if (User::findByEmail(strtolower($data['email']))) {
            $v->add('email', 'That email is already in use.');
        }
        if ($v->fails()) {
            $this->fail($v);
        }
        $roleIds = $request->input('role_ids');
        if (!is_array($roleIds) || $roleIds === []) {
            json_out(['ok' => false, 'error' => 'Pick at least one role.'], 422);
        }
        $id = User::createStaff([
            'email' => strtolower($data['email']),
            'password_hash' => password_hash($data['password'], PASSWORD_DEFAULT),
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'phone' => $data['phone'] ?? null,
            'status' => 'active',
        ]);
        $tid = (int) Auth::instance()->tenantId();
        try {
            User::attachTenant($id, $tid);
        } catch (\RuntimeException $e) {
            json_out(['ok' => false, 'error' => $e->getMessage()], 422);
        }
        User::setRoles($id, $tid, $roleIds);
        AuditLog::write('user.create', 'user', $id);
        json_out(['ok' => true, 'id' => $id]);
    }

    public function update(Request $request): void
    {
        $this->requireModule('users');
        $this->verifyCsrf($request);
        $id = (int) $request->param('id');
        $tid = (int) Auth::instance()->tenantId();
        $found = null;
        foreach (User::listForTenant($tid, false) as $row) {
            if ((int) $row['id'] === $id) {
                $found = $row;
                break;
            }
        }
        if (!$found) {
            json_out(['ok' => false, 'error' => 'Person not found.'], 404);
        }
        $data = $request->all();
        $v = (new Validator())
            ->required($data, ['first_name' => 'First name', 'last_name' => 'Last name', 'email' => 'Email'])
            ->email($data, 'email', 'Email', false);
        $password = (string) ($data['password'] ?? '');
        if ($password !== '' && strlen($password) < (int) config('security.password_min_length', 8)) {
            $v->add('password', 'Password must be at least 8 characters.');
        }
        $email = strtolower(trim((string) ($data['email'] ?? '')));
        $taken = User::findByEmail($email);
        if ($taken && (int) $taken['id'] !== $id) {
            $v->add('email', 'That email is already in use.');
        }
        if ($v->fails()) {
            $this->fail($v);
        }
        $roleIds = $request->input('role_ids');
        if (!is_array($roleIds) || $roleIds === []) {
            json_out(['ok' => false, 'error' => 'Pick at least one role.'], 422);
        }
        $roleIds = array_map('intval', $roleIds);
        if ($id === Auth::instance()->id()) {
            $adminId = 0;
            foreach (Role::all() as $role) {
                if (($role['code'] ?? '') === 'admin') {
                    $adminId = (int) $role['id'];
                    break;
                }
            }
            $hasAdmin = in_array('admin', array_map('trim', explode(',', (string) ($found['role_codes'] ?? ''))), true);
            if ($adminId && $hasAdmin && !in_array($adminId, $roleIds, true)) {
                json_out(['ok' => false, 'error' => 'You cannot remove your own admin role.'], 422);
            }
        }
        $patch = [
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'email' => $email,
            'phone' => $data['phone'] ?? null,
        ];
        if ($password !== '') {
            $patch['password_hash'] = password_hash($password, PASSWORD_DEFAULT);
        }
        User::updateStaff($id, $patch);
        User::setRoles($id, $tid, $roleIds);
        AuditLog::write('user.update', 'user', $id);
        json_out(['ok' => true, 'message' => 'Person updated.']);
    }

    public function toggle(Request $request): void
    {
        $this->requireModule('users');
        $this->verifyCsrf($request);
        $id = (int) $request->param('id');
        if ($id === Auth::instance()->id()) {
            json_out(['ok' => false, 'error' => 'You cannot deactivate yourself.'], 422);
        }
        $active = (int) $request->input('active', 0) === 1;
        $tid = (int) Auth::instance()->tenantId();
        if ($active) {
            User::activateInTenant($id, $tid);
        } else {
            User::deactivateInTenant($id, $tid);
        }
        AuditLog::write($active ? 'user.activate' : 'user.deactivate', 'user', $id);
        json_out(['ok' => true]);
    }
}
