<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/_walk_access.php';

use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Database;
use App\Core\Response;
use App\Services\AttendanceService;

$pdo = Database::connection();
$input = request_input();
$method = $_SERVER['REQUEST_METHOD'];
$action = $input['action'] ?? '';

function pyrk_attendance_member_by_qr(PDO $pdo, string $token): int
{
    $token = trim($token);
    if ($token === '') Response::error('QR token is required.');

    $stmt = $pdo->prepare('SELECT id FROM users WHERE qr_token = ? AND status = "active" LIMIT 1');
    $stmt->execute([$token]);
    $memberId = (int)$stmt->fetchColumn();
    if (!$memberId) Response::error('QR code did not match an active member.');
    return $memberId;
}

function pyrk_attendance_is_already_checked_in_message(string $message): bool
{
    return strpos(strtolower($message), 'already checked in') !== false;
}

function pyrk_attendance_normalize_user_ids($raw): array
{
    if (is_string($raw)) {
        $raw = preg_split('/[,\s]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
    }
    if (!is_array($raw)) {
        return [];
    }
    $ids = [];
    foreach ($raw as $value) {
        $id = (int)$value;
        if ($id > 0) {
            $ids[$id] = $id;
        }
    }
    return array_slice(array_values($ids), 0, 500);
}

if ($method === 'GET') {
    $actor = Auth::requireRole(['super_admin','event_manager','walk_leader']);
    if (($input['action'] ?? '') === 'resolve_qr') {
        $token = trim((string)($input['token'] ?? ''));
        if ($token === '') Response::error('QR token is required.');
        $stmt = $pdo->prepare('SELECT u.id AS user_id, u.full_name, u.email, u.mobile, mp.emergency_contact_mobile, mp.medical_conditions FROM users u LEFT JOIN member_profiles mp ON mp.user_id = u.id WHERE u.qr_token = ? AND u.status = "active" LIMIT 1');
        $stmt->execute([$token]);
        $member = $stmt->fetch();
        if (!$member) Response::error('No active member matched this QR token.', 404);
        Response::ok(['member' => $member]);
    }
    if (empty($input['walk_id'])) Response::error('walk_id is required.');
    $stmt = $pdo->prepare('SELECT u.id AS user_id, u.full_name, u.mobile, mp.emergency_contact_mobile, mp.medical_conditions, wr.registration_status, ws.signed_at AS waiver_signed_at, a.check_in_time, a.finish_time, a.status, a.verified_km FROM walk_registrations wr JOIN users u ON u.id = wr.user_id LEFT JOIN member_profiles mp ON mp.user_id = u.id LEFT JOIN waiver_signatures ws ON ws.walk_id = wr.walk_id AND ws.user_id = wr.user_id LEFT JOIN attendance a ON a.walk_id = wr.walk_id AND a.user_id = wr.user_id WHERE wr.walk_id = ? ORDER BY u.full_name');
    $walkId = (int)$input['walk_id'];
    pyrk_walk_access_require($pdo, $walkId, $actor, 'You are not assigned to this walk attendance.');
    $stmt->execute([$walkId]);
    $rows = $stmt->fetchAll();
    $svc = new AttendanceService();
    foreach ($rows as &$row) {
        $row['last_checkpoint'] = $svc->lastCheckpointMeta($walkId, (int)$row['user_id']);
        $row['partial_estimated_km'] = $svc->estimateKmFromLastCheckpoint($walkId, (int)$row['user_id']);
    }
    unset($row);

    $pointsStmt = $pdo->prepare('SELECT rp.id, rp.title, rp.point_type, rp.sort_order, rp.is_required_checkpoint FROM walks w JOIN route_points rp ON rp.route_id = w.route_id WHERE w.id = ? AND COALESCE(rp.is_required_checkpoint, 0) = 1 ORDER BY rp.sort_order, rp.id');
    $pointsStmt->execute([$walkId]);
    $routePoints = $pointsStmt->fetchAll();

    Response::ok(['attendance' => $rows, 'route_points' => $routePoints]);
}

if ($method === 'POST') {
    $actor = Auth::requireUser();
    Csrf::validateFromRequest();
    $svc = new AttendanceService();
    if ($action === 'self_checkin') {
        $walkId = (int)($input['walk_id'] ?? 0);
        if (!$walkId) Response::error('walk_id is required.');

        $walkStmt = $pdo->prepare('SELECT id, title, status, allow_self_checkin FROM walks WHERE id = ? LIMIT 1');
        $walkStmt->execute([$walkId]);
        $walk = $walkStmt->fetch();
        if (!$walk) Response::error('Walk not found.', 404);

        if ((int)($walk['allow_self_checkin'] ?? 0) !== 1) {
            Response::error('Self check-in is disabled for this walk. Please ask a Pyrk admin or walk leader to check you in.', 403);
        }

        $existing = $pdo->prepare('SELECT a.check_in_time, a.finish_time, a.status, u.full_name AS attendee_name, w.title AS walk_title FROM attendance a JOIN users u ON u.id = a.user_id JOIN walks w ON w.id = a.walk_id WHERE a.walk_id = ? AND a.user_id = ? LIMIT 1');
        $existing->execute([$walkId, (int)$actor['id']]);
        $existingAttendance = $existing->fetch(PDO::FETCH_ASSOC);
        if ($existingAttendance && (!empty($existingAttendance['finish_time']) || in_array((string)($existingAttendance['status'] ?? ''), ['finished', 'partially_completed', 'manual_verified'], true))) {
            Response::error('This walk is already finished for you. You cannot check in again.', 409);
        }
        if ($existingAttendance && !empty($existingAttendance['check_in_time'])) {
            Response::ok($existingAttendance + ['already_checked_in' => true], 'You are already checked in for this walk.');
        }

        try {
            $svc->checkIn($walkId, (int)$actor['id'], (int)$actor['id']);
            $detailStmt = $pdo->prepare('SELECT a.check_in_time, a.status, u.full_name AS attendee_name, w.title AS walk_title FROM attendance a JOIN users u ON u.id = a.user_id JOIN walks w ON w.id = a.walk_id WHERE a.walk_id = ? AND a.user_id = ? LIMIT 1');
            $detailStmt->execute([$walkId, (int)$actor['id']]);
            Response::ok($detailStmt->fetch(PDO::FETCH_ASSOC) ?: [], 'Check-in completed successfully. Welcome to the walk.');
        } catch (Throwable $e) {
            Response::error($e->getMessage(), 409);
        }
    }

    if ($action === 'self_checkpoint') {
        $walkId = (int)($input['walk_id'] ?? 0);
        $routePointId = (int)($input['route_point_id'] ?? 0);
        if (!$walkId || !$routePointId) Response::error('walk_id and route_point_id are required.');

        $walkStmt = $pdo->prepare('SELECT w.id, w.title, w.status, w.route_id FROM walks w WHERE w.id = ? LIMIT 1');
        $walkStmt->execute([$walkId]);
        $walk = $walkStmt->fetch(PDO::FETCH_ASSOC);
        if (!$walk) Response::error('Walk not found.', 404);

        $registered = $pdo->prepare('SELECT COUNT(*) FROM walk_registrations WHERE walk_id = ? AND user_id = ? AND registration_status IN ("registered", "approved")');
        $registered->execute([$walkId, (int)$actor['id']]);
        if ((int)$registered->fetchColumn() === 0) {
            Response::error('You must be registered before recording route progress.', 403);
        }

        $lat = isset($input['latitude']) && $input['latitude'] !== '' ? (float)$input['latitude'] : null;
        $lng = isset($input['longitude']) && $input['longitude'] !== '' ? (float)$input['longitude'] : null;
        $accuracy = isset($input['accuracy_meters']) && $input['accuracy_meters'] !== '' ? (float)$input['accuracy_meters'] : null;
        if ($lat === null || $lng === null) {
            Response::error('Mobile GPS location is required to complete this checkpoint. Please allow location permission and try again.', 422);
        }

        try {
            $svc->checkpoint($walkId, (int)$actor['id'], $routePointId, (int)$actor['id'], $lat, $lng, $accuracy, 'self_mobile');
            Response::ok($svc->checkpointProgress($walkId, (int)$actor['id']), 'Checkpoint completed. Your route progress has been updated.');
        } catch (Throwable $e) {
            Response::error($e->getMessage(), 409);
        }
    }

    Auth::requireRole(['super_admin','event_manager','walk_leader']);
    $walkIdForAccess = (int)($input['walk_id'] ?? 0);
    if ($walkIdForAccess > 0) {
        pyrk_walk_access_require($pdo, $walkIdForAccess, $actor, 'You are not assigned to this walk attendance.');
    }
    if ($action === 'checkin_qr') {
        $memberId = pyrk_attendance_member_by_qr($pdo, (string)($input['qr_token'] ?? ''));
        try {
            $svc->checkIn((int)$input['walk_id'], $memberId, (int)$actor['id']);
            Response::ok(['user_id' => $memberId], 'QR check-in completed.');
        } catch (Throwable $e) {
            if (pyrk_attendance_is_already_checked_in_message($e->getMessage())) {
                Response::ok(['user_id' => $memberId, 'already_checked_in' => true], 'This member is already checked in for this walk.');
            }
            Response::error($e->getMessage(), 409);
        }
    }
    if ($action === 'finish_qr') {
        $memberId = pyrk_attendance_member_by_qr($pdo, (string)($input['qr_token'] ?? ''));
        try {
            $svc->finish((int)$input['walk_id'], $memberId, (int)$actor['id']);
            Response::ok(['user_id' => $memberId], 'QR finish completed. Member marked as finished.');
        } catch (Throwable $e) {
            Response::error($e->getMessage(), 409);
        }
    }
    if ($action === 'checkpoint_qr') {
        $memberId = pyrk_attendance_member_by_qr($pdo, (string)($input['qr_token'] ?? ''));
        $routePointId = (int)($input['route_point_id'] ?? 0);
        if (!$routePointId) Response::error('Choose the station/checkpoint first.');

        $requiredPoint = $pdo->prepare('SELECT rp.id FROM walks w JOIN route_points rp ON rp.route_id = w.route_id WHERE w.id = ? AND rp.id = ? AND COALESCE(rp.is_required_checkpoint, 0) = 1 LIMIT 1');
        $requiredPoint->execute([(int)$input['walk_id'], $routePointId]);
        if (!$requiredPoint->fetchColumn()) {
            Response::error('This station is not configured to require QR check-in. Edit the route and enable "Requires QR check-in" for this station first.', 422);
        }

        try {
            $svc->checkpoint((int)$input['walk_id'], $memberId, $routePointId, (int)$actor['id'], null, null, null, 'station_qr_staff');
            Response::ok(['user_id' => $memberId, 'route_point_id' => $routePointId], 'Station checkpoint scanned successfully.');
        } catch (Throwable $e) {
            Response::error($e->getMessage(), 409);
        }
    }
    if ($action === 'checkin') {
        try {
            $svc->checkIn((int)$input['walk_id'], (int)$input['user_id'], (int)$actor['id']);
            Response::ok([], 'Member checked in.');
        } catch (Throwable $e) {
            if (pyrk_attendance_is_already_checked_in_message($e->getMessage())) {
                Response::ok(['already_checked_in' => true], 'This member is already checked in for this walk.');
            }
            Response::error($e->getMessage(), 409);
        }
    }
    if ($action === 'bulk_checkin') {
        $walkId = (int)($input['walk_id'] ?? 0);
        $userIds = pyrk_attendance_normalize_user_ids($input['user_ids'] ?? []);
        if ($walkId <= 0) Response::error('walk_id is required.');
        if (!$userIds) Response::error('Choose at least one attendee to check in.');

        $registeredStmt = $pdo->prepare('SELECT user_id FROM walk_registrations WHERE walk_id = ? AND user_id IN (' . implode(',', array_fill(0, count($userIds), '?')) . ')');
        $registeredStmt->execute(array_merge([$walkId], $userIds));
        $registeredIds = array_map('intval', $registeredStmt->fetchAll(PDO::FETCH_COLUMN));
        $registeredLookup = array_fill_keys($registeredIds, true);

        $checkedIn = 0;
        $alreadyCheckedIn = 0;
        $failed = [];

        foreach ($userIds as $userId) {
            if (empty($registeredLookup[$userId])) {
                $failed[] = ['user_id' => $userId, 'message' => 'Member is not registered for this walk.'];
                continue;
            }
            try {
                $svc->checkIn($walkId, $userId, (int)$actor['id']);
                $checkedIn++;
            } catch (Throwable $e) {
                if (pyrk_attendance_is_already_checked_in_message($e->getMessage())) {
                    $alreadyCheckedIn++;
                    continue;
                }
                $failed[] = ['user_id' => $userId, 'message' => $e->getMessage()];
            }
        }

        $parts = [];
        if ($checkedIn > 0) $parts[] = $checkedIn . ' checked in';
        if ($alreadyCheckedIn > 0) $parts[] = $alreadyCheckedIn . ' already checked in';
        if (count($failed) > 0) $parts[] = count($failed) . ' failed';
        $message = $parts ? 'Bulk manual check-in completed: ' . implode(', ', $parts) . '.' : 'No attendees were checked in.';
        $payload = [
            'checked_in' => $checkedIn,
            'already_checked_in' => $alreadyCheckedIn,
            'failed_count' => count($failed),
            'failed' => $failed,
        ];

        if ($checkedIn === 0 && $alreadyCheckedIn === 0 && count($failed) > 0) {
            $firstIssue = (string)($failed[0]['message'] ?? 'Please review the selected attendees.');
            Response::error($message . ' First issue: ' . $firstIssue, 409, ['data' => $payload]);
        }

        Response::ok($payload, $message);
    }
    if ($action === 'finish') {
        try {
            $svc->finish((int)$input['walk_id'], (int)$input['user_id'], (int)$actor['id']);
            Response::ok([], 'Member marked as finished.');
        } catch (Throwable $e) {
            Response::error($e->getMessage(), 409);
        }
    }
    if ($action === 'partial_finish_manual') {
        $km = isset($input['verified_km']) ? (float)$input['verified_km'] : (float)($input['km'] ?? 0);
        $earnedKm = $svc->partialFinish((int)$input['walk_id'], (int)$input['user_id'], (int)$actor['id'], $km, 'manual');
        Response::ok(['verified_km' => $earnedKm], 'Member marked as partially completed with ' . number_format($earnedKm, 2) . ' KM.');
    }
    if ($action === 'partial_finish_checkpoint') {
        $earnedKm = $svc->partialFinish((int)$input['walk_id'], (int)$input['user_id'], (int)$actor['id'], null, 'checkpoint');
        Response::ok(['verified_km' => $earnedKm], 'Member marked as partially completed from last checkpoint: ' . number_format($earnedKm, 2) . ' KM.');
    }
    if ($action === 'checkpoint') {
        $lat = isset($input['latitude']) && $input['latitude'] !== '' ? (float)$input['latitude'] : null;
        $lng = isset($input['longitude']) && $input['longitude'] !== '' ? (float)$input['longitude'] : null;
        $accuracy = isset($input['accuracy_meters']) && $input['accuracy_meters'] !== '' ? (float)$input['accuracy_meters'] : null;
        $svc->checkpoint((int)$input['walk_id'], (int)$input['user_id'], (int)$input['route_point_id'], (int)$actor['id'], $lat, $lng, $accuracy, 'staff');
        Response::ok([], 'Checkpoint registered.');
    }
}

Response::error('Unsupported attendance action.', 404);
