<?php
declare(strict_types=1);

require_once dirname(__DIR__) . '/lib/tenant.php';
require_once dirname(__DIR__) . '/lib/availability.php';
require_once dirname(__DIR__) . '/lib/users.php';
require_once dirname(__DIR__) . '/lib/reminders.php';
require_once dirname(__DIR__) . '/lib/payments.php';
require_once dirname(__DIR__) . '/lib/client.php';

header('Content-Type: application/json; charset=utf-8');

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

$action = $_GET['action'] ?? $_POST['action'] ?? 'list';

try {
    if ($action === 'quote_public') {
        $tenant = resolve_tenant_from_host();
        if (!$tenant) {
            json_response(['ok' => false, 'error' => 'Shop not found'], 404);
        }
        $tenantId = (int) $tenant['id'];
        $serviceId = (int) ($_GET['service_id'] ?? 0);
        $svc = db()->prepare('SELECT * FROM services WHERE id = ? AND tenant_id = ?');
        $svc->execute([$serviceId, $tenantId]);
        $service = $svc->fetch();
        if (!$service) {
            json_response(['ok' => false, 'error' => 'Invalid service'], 400);
        }
        $due = payment_booking_due($tenantId, $service);
        $methods = payment_available_methods($tenantId, 'booking');
        // If online amount is 0 / off, only manual (or empty) — unless demo exposes advance pay.
        if (!payment_is_demo_environment()
            && ($due['amount'] <= 0 || ($due['purpose'] === 'none' && !$due['optional'] && !$due['required']))) {
            $methods = array_values(array_filter($methods, static fn ($m) => $m['id'] === 'manual'));
            if (!$methods) {
                $methods = [['id' => 'manual', 'label' => 'Pay at the chair', 'hint' => 'No online payment required']];
            }
        }
        json_response([
            'ok' => true,
            'due' => $due,
            'methods' => $methods,
            'demo' => payment_is_demo_environment(),
        ]);
    }

    if ($action === 'create_public') {
        $tenant = resolve_tenant_from_host();
        if (!$tenant) {
            json_response(['ok' => false, 'error' => 'Shop not found'], 404);
        }
        $tenantId = (int) $tenant['id'];
        $tenantParam = tenant_param();
        $serviceId = (int) ($_POST['service_id'] ?? 0);
        $staffId = ($_POST['staff_id'] ?? '') !== '' ? (int) $_POST['staff_id'] : null;
        $name = trim((string) ($_POST['customer_name'] ?? ''));
        $phone = trim((string) ($_POST['customer_phone'] ?? ''));
        $email = trim((string) ($_POST['customer_email'] ?? ''));
        $carrierId = ($_POST['carrier_id'] ?? '') !== '' ? (int) $_POST['carrier_id'] : null;
        $date = trim((string) ($_POST['date'] ?? ''));
        $time = trim((string) ($_POST['time'] ?? ''));
        $notes = trim((string) ($_POST['notes'] ?? ''));
        $payMethod = trim((string) ($_POST['payment_method'] ?? 'manual'));

        if ($name === '' || $serviceId <= 0 || $date === '' || $time === '') {
            json_response(['ok' => false, 'error' => 'Name, service, date, and time slot are required.'], 400);
        }
        if (!user_normalize_email($email)) {
            json_response(['ok' => false, 'error' => 'A valid email is required for appointment reminders.'], 400);
        }

        if (preg_match('/^\d{1,2}:\d{2}/', $time, $m)) {
            $time = substr($time, 0, 5);
            if (strlen($time) === 4) {
                $time = '0' . $time;
            }
        }

        $svc = db()->prepare('SELECT * FROM services WHERE id = ? AND tenant_id = ?');
        $svc->execute([$serviceId, $tenantId]);
        $service = $svc->fetch();
        if (!$service) {
            json_response(['ok' => false, 'error' => 'Invalid service'], 400);
        }

        $due = payment_booking_due($tenantId, $service);
        $methods = payment_available_methods($tenantId, 'booking');
        $allowed = array_column($methods, 'id');
        $isDemoPay = $payMethod === 'demo' && payment_is_demo_environment();
        if ($due['amount'] <= 0 && !$due['required'] && !$due['optional'] && !$isDemoPay) {
            $payMethod = 'manual';
            $allowed = ['manual'];
        }
        if ($isDemoPay) {
            $allowed[] = 'demo';
        }
        if ($due['required'] && $payMethod === 'manual' && !in_array('manual', $allowed, true)) {
            json_response(['ok' => false, 'error' => 'Online payment is required for this booking. Choose Stripe or PayPal.'], 400);
        }
        if ($due['required'] && $due['amount'] > 0 && $payMethod === 'manual') {
            // Shop mode requires deposit/full but somehow manual still listed — block
            $online = array_values(array_filter($allowed, static fn ($id) => $id !== 'manual' && $id !== 'demo'));
            if ($online) {
                json_response(['ok' => false, 'error' => 'Please pay online to hold this appointment.'], 400);
            }
        }
        if (!in_array($payMethod, $allowed, true) && !($payMethod === 'manual' && !$due['required'])) {
            json_response(['ok' => false, 'error' => 'Choose a valid payment method.'], 400);
        }

        $starts = DateTime::createFromFormat('Y-m-d H:i', $date . ' ' . $time);
        if (!$starts) {
            json_response(['ok' => false, 'error' => 'Invalid date/time'], 400);
        }

        $resolvedStaff = availability_resolve_staff_for_slot($tenantId, $serviceId, $date, $time, $staffId);
        if (!$resolvedStaff) {
            json_response(['ok' => false, 'error' => 'That time is no longer available. Pick another slot.'], 409);
        }

        if (!availability_is_bookable($tenantId, $serviceId, $starts->format('Y-m-d H:i:s'), $resolvedStaff)) {
            json_response(['ok' => false, 'error' => 'That time is no longer available. Pick another slot.'], 409);
        }

        $ends = clone $starts;
        $ends->modify('+' . (int) $service['duration_minutes'] . ' minutes');

        $user = user_upsert($tenantId, $name, $phone ?: null, $email ?: null, $carrierId, null);

        $staffName = '';
        $sn = db()->prepare('SELECT name FROM staff_barbers WHERE id = ? AND tenant_id = ?');
        $sn->execute([$resolvedStaff, $tenantId]);
        $staffName = (string) ($sn->fetchColumn() ?: '');

        // Demo "pay in advance" books normally then shows mock processor modal (no real charge).
        if ($isDemoPay) {
            $noteDemo = trim($notes . "\n[Demo] Client chose pay in advance — checkout simulated.");
            db()->prepare(
                'INSERT INTO bookings (tenant_id, service_id, staff_id, user_id, customer_name, customer_phone, starts_at, ends_at, status, notes, payment_status, deposit_paid)
                 VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'
            )->execute([
                $tenantId,
                $serviceId,
                $resolvedStaff,
                (int) $user['id'],
                $name,
                user_normalize_phone($phone) ?: ($phone ?: null),
                $starts->format('Y-m-d H:i:s'),
                $ends->format('Y-m-d H:i:s'),
                'pending',
                $noteDemo ?: null,
                'pay_at_chair',
                0,
            ]);
            $bookingId = (int) db()->lastInsertId();
            reminder_schedule_for_booking($bookingId);
            json_response([
                'ok' => true,
                'id' => $bookingId,
                'user_id' => (int) $user['id'],
                'staff_id' => $resolvedStaff,
                'staff_name' => $staffName,
                'service_name' => (string) $service['name'],
                'customer_name' => $name,
                'customer_email' => $email,
                'starts_at' => $starts->format('Y-m-d H:i:s'),
                'demo_checkout' => true,
                'due' => $due,
                'status' => 'pending',
                'message' => 'Appointment held. Demo payment screen follows.',
            ]);
        }

        $needsOnline = $payMethod !== 'manual' && $payMethod !== 'demo' && $due['amount'] > 0;
        $bookingStatus = $needsOnline ? 'awaiting_payment' : 'pending';
        $payStatus = $needsOnline ? 'pending' : ($payMethod === 'manual' ? 'pay_at_chair' : 'unpaid');

        db()->prepare(
            'INSERT INTO bookings (tenant_id, service_id, staff_id, user_id, customer_name, customer_phone, starts_at, ends_at, status, notes, payment_status, deposit_paid)
             VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'
        )->execute([
            $tenantId,
            $serviceId,
            $resolvedStaff,
            (int) $user['id'],
            $name,
            user_normalize_phone($phone) ?: ($phone ?: null),
            $starts->format('Y-m-d H:i:s'),
            $ends->format('Y-m-d H:i:s'),
            $bookingStatus,
            $notes ?: null,
            $payStatus,
            0,
        ]);

        $bookingId = (int) db()->lastInsertId();
        $checkoutUrl = null;
        $paymentId = null;

        if ($needsOnline) {
            $started = payment_start_checkout(
                $tenantId,
                $payMethod,
                (string) $due['purpose'],
                'booking',
                $bookingId,
                (float) $due['amount'],
                (string) $due['currency'],
                'Appointment · ' . $service['name'] . ' · ' . $starts->format('M j g:ia'),
                $email,
                $tenantParam
            );
            $paymentId = $started['payment_id'];
            $checkoutUrl = $started['checkout_url'];
        } else {
            reminder_schedule_for_booking($bookingId);
        }

        json_response([
            'ok' => true,
            'id' => $bookingId,
            'user_id' => (int) $user['id'],
            'staff_id' => $resolvedStaff,
            'staff_name' => $staffName,
            'service_name' => (string) $service['name'],
            'customer_name' => $name,
            'customer_email' => $email,
            'starts_at' => $starts->format('Y-m-d H:i:s'),
            'payment_id' => $paymentId,
            'checkout_url' => $checkoutUrl,
            'due' => $due,
            'status' => $bookingStatus,
            'demo_checkout' => false,
        ]);
    }

    if (empty($_SESSION['admin_logged_in'])) {
        json_response(['ok' => false, 'error' => 'Unauthorized'], 401);
    }

    $tenantId = current_tenant_id();

    if ($action === 'update_status') {
        $id = (int) ($_POST['id'] ?? 0);
        $status = (string) ($_POST['status'] ?? 'pending');
        if (!in_array($status, ['pending', 'confirmed', 'completed', 'cancelled', 'awaiting_payment', 'no_show'], true)) {
            json_response(['ok' => false, 'error' => 'Invalid status'], 400);
        }
        db()->prepare('UPDATE bookings SET status = ? WHERE id = ? AND tenant_id = ?')
            ->execute([$status, $id, $tenantId]);
        if (in_array($status, ['cancelled', 'completed', 'no_show'], true)) {
            reminder_cancel_pending($id);
        } else {
            reminder_schedule_for_booking($id);
        }
        json_response(['ok' => true]);
    }

    if ($action === 'reschedule') {
        $id = (int) ($_POST['id'] ?? $_POST['booking_id'] ?? 0);
        $date = trim((string) ($_POST['date'] ?? ''));
        $time = trim((string) ($_POST['time'] ?? ''));
        $staffRaw = $_POST['staff_id'] ?? null;
        $staffId = ($staffRaw !== null && $staffRaw !== '') ? (int) $staffRaw : null;

        $stmt = db()->prepare('SELECT * FROM bookings WHERE id = ? AND tenant_id = ?');
        $stmt->execute([$id, $tenantId]);
        $booking = $stmt->fetch();
        if (!$booking) {
            json_response(['ok' => false, 'error' => 'Booking not found'], 404);
        }
        if (in_array((string) $booking['status'], ['cancelled', 'completed'], true)) {
            json_response(['ok' => false, 'error' => 'Cannot reschedule a cancelled or completed booking.'], 400);
        }
        if ($date === '' || $time === '') {
            json_response(['ok' => false, 'error' => 'Pick a new date and time slot.'], 400);
        }
        if (preg_match('/^\d{1,2}:\d{2}/', $time, $m)) {
            $time = substr($time, 0, 5);
            if (strlen($time) === 4) {
                $time = '0' . $time;
            }
        }

        $serviceId = (int) $booking['service_id'];
        $starts = DateTime::createFromFormat('Y-m-d H:i', $date . ' ' . $time);
        if (!$starts) {
            json_response(['ok' => false, 'error' => 'Invalid date/time'], 400);
        }

        $resolvedStaff = availability_resolve_staff_for_slot($tenantId, $serviceId, $date, $time, $staffId, $id);
        if (!$resolvedStaff) {
            json_response(['ok' => false, 'error' => 'That time is not available for the selected barber.'], 409);
        }
        if (!availability_is_bookable($tenantId, $serviceId, $starts->format('Y-m-d H:i:s'), $resolvedStaff, $id)) {
            json_response(['ok' => false, 'error' => 'That time is no longer available. Pick another slot.'], 409);
        }

        $svc = db()->prepare('SELECT duration_minutes FROM services WHERE id = ? AND tenant_id = ?');
        $svc->execute([$serviceId, $tenantId]);
        $service = $svc->fetch();
        if (!$service) {
            json_response(['ok' => false, 'error' => 'Service missing'], 400);
        }
        $ends = clone $starts;
        $ends->modify('+' . (int) $service['duration_minutes'] . ' minutes');

        $note = trim((string) ($booking['notes'] ?? '') . "\n[Shop reschedule] "
            . date('Y-m-d g:ia', strtotime((string) $booking['starts_at']))
            . ' → ' . $starts->format('Y-m-d g:ia'));

        $newStatus = (string) $booking['status'];
        if ($newStatus === 'awaiting_payment') {
            $newStatus = 'awaiting_payment';
        } elseif ($newStatus === 'pending') {
            $newStatus = 'pending';
        } else {
            $newStatus = 'confirmed';
        }

        db()->prepare(
            'UPDATE bookings SET staff_id=?, starts_at=?, ends_at=?, notes=?, status=? WHERE id=? AND tenant_id=?'
        )->execute([
            $resolvedStaff,
            $starts->format('Y-m-d H:i:s'),
            $ends->format('Y-m-d H:i:s'),
            $note,
            $newStatus,
            $id,
            $tenantId,
        ]);
        reminder_schedule_for_booking($id);
        json_response([
            'ok' => true,
            'id' => $id,
            'staff_id' => $resolvedStaff,
            'starts_at' => $starts->format('Y-m-d H:i:s'),
            'ends_at' => $ends->format('Y-m-d H:i:s'),
            'status' => $newStatus,
        ]);
    }

    if ($action === 'mark_no_show') {
        $id = (int) ($_POST['id'] ?? 0);
        $stmt = db()->prepare('SELECT * FROM bookings WHERE id = ? AND tenant_id = ?');
        $stmt->execute([$id, $tenantId]);
        $booking = $stmt->fetch();
        if (!$booking) {
            json_response(['ok' => false, 'error' => 'Booking not found'], 404);
        }
        $policy = client_policy($tenantId);
        $fee = (float) ($policy['cancel_fee'] ?? 0);
        $note = trim((string) ($booking['notes'] ?? '') . "\n[No-show] Fee noted: $" . number_format($fee, 2));
        db()->prepare('UPDATE bookings SET status = ?, notes = ?, payment_status = ? WHERE id = ? AND tenant_id = ?')
            ->execute(['no_show', $note, $fee > 0 ? 'fee_owed' : ($booking['payment_status'] ?? 'unpaid'), $id, $tenantId]);
        reminder_cancel_pending($id);
        json_response(['ok' => true, 'fee' => $fee]);
    }

    if ($action === 'waive_fee') {
        $id = (int) ($_POST['id'] ?? 0);
        $stmt = db()->prepare('SELECT notes, payment_status FROM bookings WHERE id = ? AND tenant_id = ?');
        $stmt->execute([$id, $tenantId]);
        $booking = $stmt->fetch();
        if (!$booking) {
            json_response(['ok' => false, 'error' => 'Booking not found'], 404);
        }
        $note = trim((string) ($booking['notes'] ?? '') . "\n[Fee waived by admin]");
        db()->prepare('UPDATE bookings SET notes = ?, payment_status = ? WHERE id = ? AND tenant_id = ?')
            ->execute([$note, 'waived', $id, $tenantId]);
        json_response(['ok' => true]);
    }

    if ($action === 'charge_fee') {
        $id = (int) ($_POST['id'] ?? 0);
        $provider = trim((string) ($_POST['provider'] ?? 'stripe'));
        $tenantParam = tenant_param();
        $stmt = db()->prepare(
            'SELECT b.*, s.name AS service_name FROM bookings b
             LEFT JOIN services s ON s.id = b.service_id
             WHERE b.id = ? AND b.tenant_id = ?'
        );
        $stmt->execute([$id, $tenantId]);
        $booking = $stmt->fetch();
        if (!$booking) {
            json_response(['ok' => false, 'error' => 'Booking not found'], 404);
        }
        $policy = client_policy($tenantId);
        $fee = (float) ($policy['cancel_fee'] ?? 0);
        if ($fee <= 0) {
            json_response(['ok' => false, 'error' => 'Cancel fee is $0 in Client Policy.'], 400);
        }
        if (!in_array($provider, ['stripe', 'paypal'], true)) {
            json_response(['ok' => false, 'error' => 'Choose stripe or paypal'], 400);
        }
        $purpose = (($booking['status'] ?? '') === 'no_show') ? 'no_show' : 'cancel_fee';
        $started = payment_start_checkout(
            $tenantId,
            $provider,
            $purpose,
            'booking',
            $id,
            $fee,
            (string) (payment_settings($tenantId)['default_currency'] ?? 'USD'),
            ucfirst(str_replace('_', ' ', $purpose)) . ' · booking #' . $id,
            null,
            $tenantParam
        );
        db()->prepare('UPDATE bookings SET payment_status = ? WHERE id = ? AND tenant_id = ?')
            ->execute(['fee_checkout', $id, $tenantId]);
        json_response(['ok' => true, 'checkout_url' => $started['checkout_url'], 'payment_id' => $started['payment_id'], 'fee' => $fee]);
    }

    if ($action === 'refund_payment') {
        $paymentId = (int) ($_POST['payment_id'] ?? 0);
        $note = trim((string) ($_POST['note'] ?? 'Admin refund'));
        payment_mark_refunded($paymentId, $tenantId, $note);
        json_response(['ok' => true]);
    }

    if ($action === 'delete') {
        $id = (int) ($_POST['id'] ?? 0);
        reminder_cancel_pending($id);
        db()->prepare('DELETE FROM bookings WHERE id = ? AND tenant_id = ?')->execute([$id, $tenantId]);
        json_response(['ok' => true]);
    }

    if ($action === 'create') {
        $serviceId = (int) ($_POST['service_id'] ?? 0);
        $staffId = ($_POST['staff_id'] ?? '') !== '' ? (int) $_POST['staff_id'] : null;
        $name = trim((string) ($_POST['customer_name'] ?? ''));
        $phone = trim((string) ($_POST['customer_phone'] ?? ''));
        $email = trim((string) ($_POST['customer_email'] ?? ''));
        $startsAt = trim((string) ($_POST['starts_at'] ?? ''));
        $status = (string) ($_POST['status'] ?? 'confirmed');
        if ($name === '' || $serviceId <= 0 || $startsAt === '') {
            json_response(['ok' => false, 'error' => 'Missing fields'], 400);
        }
        $svc = db()->prepare('SELECT duration_minutes FROM services WHERE id = ? AND tenant_id = ?');
        $svc->execute([$serviceId, $tenantId]);
        $service = $svc->fetch();
        if (!$service) {
            json_response(['ok' => false, 'error' => 'Invalid service'], 400);
        }
        $starts = new DateTime($startsAt);
        $ends = clone $starts;
        $ends->modify('+' . (int) $service['duration_minutes'] . ' minutes');
        $user = user_upsert($tenantId, $name, $phone ?: null, $email ?: null);
        db()->prepare(
            'INSERT INTO bookings (tenant_id, service_id, staff_id, user_id, customer_name, customer_phone, starts_at, ends_at, status, payment_status)
             VALUES (?,?,?,?,?,?,?,?,?,?)'
        )->execute([
            $tenantId, $serviceId, $staffId, (int) $user['id'], $name,
            user_normalize_phone($phone) ?: ($phone ?: null),
            $starts->format('Y-m-d H:i:s'), $ends->format('Y-m-d H:i:s'), $status, 'pay_at_chair',
        ]);
        $bookingId = (int) db()->lastInsertId();
        reminder_schedule_for_booking($bookingId);
        json_response(['ok' => true, 'id' => $bookingId, 'user_id' => (int) $user['id']]);
    }

    $from = $_GET['from'] ?? date('Y-m-01');
    $to = $_GET['to'] ?? date('Y-m-t');
    $stmt = db()->prepare(
        'SELECT b.*, s.name AS service_name, sb.name AS staff_name
         FROM bookings b
         LEFT JOIN services s ON s.id = b.service_id
         LEFT JOIN staff_barbers sb ON sb.id = b.staff_id
         WHERE b.tenant_id = ? AND b.starts_at >= ? AND b.starts_at < DATE_ADD(?, INTERVAL 1 DAY)
         ORDER BY b.starts_at'
    );
    $stmt->execute([$tenantId, $from . ' 00:00:00', $to]);
    $rows = $stmt->fetchAll();
    json_response(['ok' => true, 'bookings' => $rows]);
} catch (Throwable $e) {
    json_response(['ok' => false, 'error' => $e->getMessage()], 400);
}
