<?php

$tokenRec = require_valid_token_web();

$callerRole     = $tokenRec['role'];
$callerEntityID = $tokenRec['entityID'] ?? '';

switch ($method) {

case 'GET':
    try {
        [$pdo, $tmpDb, $lockFp] = qdb_open_read_or_fail();

        if ($callerRole === 'admin') {
            $users = $pdo->query(
                "SELECT u.userID, u.username, u.role, u.entityID, u.mustChangePassword, u.createdAt,
                        COALESCE(a.location, s.location, '') AS location,
                        c.supervisorID,
                        sv.username AS supervisorUsername
                 FROM users u
                 LEFT JOIN admin      a  ON a.adminID      = u.entityID AND u.role = 'admin'
                 LEFT JOIN supervisor s  ON s.supervisorID = u.entityID AND u.role = 'supervisor'
                 LEFT JOIN coach      c  ON c.coachID      = u.entityID AND u.role = 'coach'
                 LEFT JOIN supervisor sv ON sv.supervisorID = c.supervisorID
                 ORDER BY u.role, u.username"
            )->fetchAll(PDO::FETCH_ASSOC);

            $supervisors = $pdo->query(
                "SELECT supervisorID, username, location FROM supervisor ORDER BY username"
            )->fetchAll(PDO::FETCH_ASSOC);
        } else {
            $svNameStmt = $pdo->prepare("SELECT username FROM supervisor WHERE supervisorID = :svid");
            $svNameStmt->execute([':svid' => $callerEntityID]);
            $svName = $svNameStmt->fetchColumn() ?: '';

            $stmt = $pdo->prepare(
                "SELECT u.userID, u.username, u.role, u.entityID, u.mustChangePassword, u.createdAt,
                        '' AS location, c.supervisorID, :svname AS supervisorUsername
                 FROM users u
                 JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach'
                 WHERE c.supervisorID = :svid
                 ORDER BY u.username"
            );
            $stmt->execute([':svid' => $callerEntityID, ':svname' => $svName]);
            $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
            $supervisors = [];
        }

        qdb_discard($tmpDb, $lockFp);
        json_success([
            'users'       => $users,
            'supervisors' => $supervisors,
            'callerRole'  => $callerRole,
        ]);
    } catch (Throwable $e) {
        qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null);
    }
    break;

case 'POST':
    $body         = read_json_body();
    $username     = trim($body['username'] ?? '');
    $password     = (string)($body['password'] ?? '');
    $role         = strtolower(trim($body['role'] ?? ''));
    $location     = trim($body['location'] ?? '');
    $supervisorID = trim($body['supervisorID'] ?? '');
    $mustChange   = isset($body['mustChangePassword']) ? (int)(bool)$body['mustChangePassword'] : 1;

    if ($username === '' || $password === '' || $role === '') {
        json_error('MISSING_FIELDS', 'username, password, and role are required', 400);
    }
    if (!in_array($role, ['admin', 'supervisor', 'coach'], true)) {
        json_error('INVALID_ROLE', 'role must be admin, supervisor, or coach', 400);
    }
    if (strlen($password) < 6) {
        json_error('PASSWORD_TOO_SHORT', 'Password must be at least 6 characters', 400);
    }
    if ($callerRole === 'supervisor' && $role !== 'coach') {
        json_error('FORBIDDEN', 'Supervisors may only create coaches', 403);
    }
    if ($callerRole === 'supervisor') {
        $supervisorID = $callerEntityID;
    }
    if ($role === 'coach' && $supervisorID === '') {
        json_error('MISSING_FIELDS', 'supervisorID is required for coaches', 400);
    }

    try {
        [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();

        $chk = $pdo->prepare("SELECT userID FROM users WHERE username = :u");
        $chk->execute([':u' => $username]);
        if ($chk->fetch()) {
            qdb_discard($tmpDb, $lockFp);
            json_error('DUPLICATE', 'Username already exists', 409);
        }

        if ($role === 'coach') {
            $svCheck = $pdo->prepare("SELECT supervisorID FROM supervisor WHERE supervisorID = :sid");
            $svCheck->execute([':sid' => $supervisorID]);
            if (!$svCheck->fetch()) {
                qdb_discard($tmpDb, $lockFp);
                json_error('NOT_FOUND', 'Supervisor not found', 400);
            }
            if ($callerRole === 'supervisor' && $supervisorID !== $callerEntityID) {
                qdb_discard($tmpDb, $lockFp);
                json_error('FORBIDDEN', 'Cannot create coaches for another supervisor', 403);
            }
        }

        $entityID = bin2hex(random_bytes(16));
        $userID   = bin2hex(random_bytes(16));
        $hash     = password_hash($password, PASSWORD_DEFAULT);
        $now      = time();

        $pdo->beginTransaction();
        switch ($role) {
            case 'admin':
                $pdo->prepare("INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)")
                    ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]);
                break;
            case 'supervisor':
                $pdo->prepare("INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)")
                    ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]);
                break;
            case 'coach':
                $pdo->prepare("INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)")
                    ->execute([':id' => $entityID, ':sid' => $supervisorID, ':u' => $username]);
                break;
        }
        $pdo->prepare(
            "INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt)
             VALUES (:uid, :u, :hash, :role, :eid, :mcp, :now)"
        )->execute([
            ':uid' => $userID, ':u' => $username, ':hash' => $hash,
            ':role' => $role, ':eid' => $entityID, ':mcp' => $mustChange, ':now' => $now,
        ]);
        $pdo->commit();
        qdb_save($tmpDb, $lockFp);

        $svUsername = null;
        if ($role === 'coach') {
            [$pdo2, $tmp2, $lk2] = qdb_open_read_or_fail();
            $svr = $pdo2->prepare("SELECT username FROM supervisor WHERE supervisorID = :sid");
            $svr->execute([':sid' => $supervisorID]);
            $svUsername = $svr->fetchColumn() ?: null;
            qdb_discard($tmp2, $lk2);
        }

        json_success([
            'userID'             => $userID,
            'entityID'           => $entityID,
            'username'           => $username,
            'role'               => $role,
            'location'           => $location,
            'supervisorID'       => $role === 'coach' ? $supervisorID : null,
            'supervisorUsername'  => $svUsername,
            'mustChangePassword' => $mustChange,
            'createdAt'          => $now,
        ]);
    } catch (Throwable $e) {
        qdb_handler_fail($e, 'users', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null);
    }
    break;

case 'PUT':
case 'PATCH':
    $body         = read_json_body();
    $targetUserID = trim($body['userID'] ?? '');
    $action       = trim((string)($body['action'] ?? ''));
    $newPassword  = (string)($body['password'] ?? $body['newPassword'] ?? '');

    if ($action === 'revokeSessions') {
        if ($targetUserID === '') {
            json_error('MISSING_FIELDS', 'userID is required', 400);
        }
        if ($targetUserID === ($tokenRec['userID'] ?? '')) {
            json_error('FORBIDDEN', 'Use Admin settings to revoke your own other sessions, or sign out', 400);
        }

        try {
            [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();

            $row = $pdo->prepare('SELECT userID, username, role, entityID FROM users WHERE userID = :uid');
            $row->execute([':uid' => $targetUserID]);
            $target = $row->fetch(PDO::FETCH_ASSOC);

            if (!$target) {
                qdb_discard($tmpDb, $lockFp);
                json_error('NOT_FOUND', 'User not found', 404);
            }

            $targetRole = $target['role'] ?? '';
            if ($callerRole === 'admin') {
                if (!in_array($targetRole, ['admin', 'supervisor', 'coach'], true)) {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Cannot revoke sessions for this user', 403);
                }
            } elseif ($callerRole === 'supervisor') {
                if ($targetRole !== 'coach') {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Supervisors may only revoke sessions for their coaches', 403);
                }
                $chk = $pdo->prepare(
                    'SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid'
                );
                $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]);
                if (!$chk->fetch()) {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Counselor is not under your supervision', 403);
                }
            } else {
                qdb_discard($tmpDb, $lockFp);
                json_error('FORBIDDEN', 'Not allowed to revoke sessions', 403);
            }

            $removed = token_revoke_all_for_user_on_pdo($pdo, $targetUserID);
            qdb_save($tmpDb, $lockFp);

            json_success([
                'userID'          => $targetUserID,
                'username'        => $target['username'],
                'revokedSessions' => $removed,
            ]);
        } catch (Throwable $e) {
            qdb_handler_fail($e, 'Revoke user sessions', null, $tmpDb ?? null, $lockFp ?? null);
        }
        break;
    }

    if ($newPassword !== '') {
        if ($targetUserID === '') {
            json_error('MISSING_FIELDS', 'userID is required', 400);
        }
        if ($targetUserID === ($tokenRec['userID'] ?? '')) {
            json_error('FORBIDDEN', 'Cannot reset your own password', 400);
        }
        if (strlen($newPassword) < 6) {
            json_error('PASSWORD_TOO_SHORT', 'Password must be at least 6 characters', 400);
        }
        $mustChange = isset($body['mustChangePassword']) ? (int)(bool)$body['mustChangePassword'] : 1;

        try {
            [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();

            $row = $pdo->prepare(
                "SELECT u.userID, u.username, u.role, u.entityID, c.supervisorID
                 FROM users u
                 LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach'
                 WHERE u.userID = :uid"
            );
            $row->execute([':uid' => $targetUserID]);
            $target = $row->fetch(PDO::FETCH_ASSOC);

            if (!$target) {
                qdb_discard($tmpDb, $lockFp);
                json_error('NOT_FOUND', 'User not found', 404);
            }

            $targetRole = $target['role'] ?? '';
            if ($callerRole === 'admin') {
                if (!in_array($targetRole, ['supervisor', 'coach'], true)) {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Admins may only reset passwords for supervisors and coaches', 403);
                }
            } elseif ($callerRole === 'supervisor') {
                if ($targetRole !== 'coach') {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Supervisors may only reset passwords for their coaches', 403);
                }
                $chk = $pdo->prepare(
                    'SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid'
                );
                $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]);
                if (!$chk->fetch()) {
                    qdb_discard($tmpDb, $lockFp);
                    json_error('FORBIDDEN', 'Counselor is not under your supervision', 403);
                }
            } else {
                qdb_discard($tmpDb, $lockFp);
                json_error('FORBIDDEN', 'Not allowed to reset passwords', 403);
            }

            $hash = password_hash($newPassword, PASSWORD_DEFAULT);
            $pdo->prepare(
                'UPDATE users SET passwordHash = :h, mustChangePassword = :mcp WHERE userID = :uid'
            )->execute([':h' => $hash, ':mcp' => $mustChange, ':uid' => $targetUserID]);

            token_revoke_all_for_user_on_pdo($pdo, $targetUserID);

            qdb_save($tmpDb, $lockFp);

            json_success([
                'userID'             => $targetUserID,
                'username'           => $target['username'],
                'mustChangePassword' => $mustChange,
            ]);
        } catch (Throwable $e) {
        qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null);
    }
        break;
    }

    if ($callerRole !== 'admin') {
        json_error('FORBIDDEN', 'Only admins can reassign coaches', 403);
    }

    $supervisorID = trim($body['supervisorID'] ?? '');

    if ($targetUserID === '' || $supervisorID === '') {
        json_error('MISSING_FIELDS', 'userID and supervisorID are required', 400);
    }

    try {
        [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();

        $row = $pdo->prepare(
            "SELECT u.userID, u.username, u.role, u.entityID, c.supervisorID
             FROM users u
             LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach'
             WHERE u.userID = :uid"
        );
        $row->execute([':uid' => $targetUserID]);
        $target = $row->fetch(PDO::FETCH_ASSOC);

        if (!$target) {
            qdb_discard($tmpDb, $lockFp);
            json_error('NOT_FOUND', 'User not found', 404);
        }
        if (($target['role'] ?? '') !== 'coach') {
            qdb_discard($tmpDb, $lockFp);
            json_error('INVALID_FIELD', 'Only coaches can be reassigned to a supervisor', 400);
        }

        $svCheck = $pdo->prepare('SELECT username FROM supervisor WHERE supervisorID = :sid');
        $svCheck->execute([':sid' => $supervisorID]);
        $svUsername = $svCheck->fetchColumn();
        if ($svUsername === false) {
            qdb_discard($tmpDb, $lockFp);
            json_error('NOT_FOUND', 'Supervisor not found', 404);
        }

        if (($target['supervisorID'] ?? '') === $supervisorID) {
            qdb_discard($tmpDb, $lockFp);
            json_success([
                'userID'             => $targetUserID,
                'supervisorID'       => $supervisorID,
                'supervisorUsername' => $svUsername,
            ]);
        }

        $pdo->prepare('UPDATE coach SET supervisorID = :sid WHERE coachID = :cid')
            ->execute([':sid' => $supervisorID, ':cid' => $target['entityID']]);

        qdb_save($tmpDb, $lockFp);

        json_success([
            'userID'             => $targetUserID,
            'supervisorID'       => $supervisorID,
            'supervisorUsername' => $svUsername,
        ]);
    } catch (Throwable $e) {
        qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null);
    }
    break;

case 'DELETE':
    $body         = read_json_body();
    $targetUserID = trim($body['userID'] ?? '');

    if ($targetUserID === '') {
        json_error('MISSING_FIELDS', 'userID is required', 400);
    }
    if ($targetUserID === ($tokenRec['userID'] ?? '')) {
        json_error('FORBIDDEN', 'Cannot delete your own account', 400);
    }

    try {
        [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();

        $row = $pdo->prepare("SELECT role, entityID FROM users WHERE userID = :uid");
        $row->execute([':uid' => $targetUserID]);
        $target = $row->fetch(PDO::FETCH_ASSOC);

        if (!$target) {
            qdb_discard($tmpDb, $lockFp);
            json_error('NOT_FOUND', 'User not found', 404);
        }

        if ($callerRole === 'supervisor') {
            if ($target['role'] !== 'coach') {
                qdb_discard($tmpDb, $lockFp);
                json_error('FORBIDDEN', 'Supervisors can only delete coaches', 403);
            }
            $chk = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid");
            $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]);
            if (!$chk->fetch()) {
                qdb_discard($tmpDb, $lockFp);
                json_error('FORBIDDEN', 'Counselor is not under your supervision', 403);
            }
        }

        $pdo->beginTransaction();
        $pdo->prepare("DELETE FROM users WHERE userID = :uid")->execute([':uid' => $targetUserID]);
        switch ($target['role']) {
            case 'admin':
                $pdo->prepare("DELETE FROM admin WHERE adminID = :id")->execute([':id' => $target['entityID']]);
                break;
            case 'supervisor':
                $pdo->prepare("DELETE FROM supervisor WHERE supervisorID = :id")->execute([':id' => $target['entityID']]);
                break;
            case 'coach':
                $pdo->prepare("DELETE FROM coach WHERE coachID = :id")->execute([':id' => $target['entityID']]);
                break;
        }
        $pdo->commit();
        qdb_save($tmpDb, $lockFp);

        json_success([]);
    } catch (Throwable $e) {
        qdb_handler_fail($e, 'users', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null);
    }
    break;

default:
    json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
}
