<?php
require_auth();
$method = $_SERVER['REQUEST_METHOD'];
$d = input();
$id = (int)($d['id'] ?? 0);
$bookingStatuses = ['pending','confirmed','checked_in','checked_out','cancelled','no_show'];
$paymentStatuses = ['unpaid','partial','paid','refunded'];

if ($method === 'GET') {
    require_permission('bookings.view');
    if ($id) {
        $s = db()->prepare('SELECT b.*, p.name AS property_name FROM bookings b JOIN properties p ON p.id=b.property_id WHERE b.id=?');
        $s->execute([$id]);
        $b = $s->fetch();
        if (!$b) json_error('Not found',404);
        $pays = db()->prepare('SELECT * FROM payments WHERE booking_id=? ORDER BY created_at DESC');
        $pays->execute([$id]);
        $b['payments'] = $pays->fetchAll();
        json_ok($b);
    }
    $where = ['1=1']; $params = [];
    if (!empty($d['property_id'])) { $where[]='b.property_id=?'; $params[]=(int)$d['property_id']; }
    if (!empty($d['status'])) { $where[]='b.status=?'; $params[]=v_enum($d['status'], $bookingStatuses, 'status'); }
    if (!empty($d['from'])) { $where[]='b.check_out>=?'; $params[]=v_date($d['from'], 'from'); }
    if (!empty($d['to'])) { $where[]='b.check_in<=?'; $params[]=v_date($d['to'], 'to'); }
    if (!empty($d['q'])) { $where[]='(b.guest_name LIKE ? OR b.reference LIKE ?)'; $q='%'.v_str($d,'q',80).'%'; $params[]=$q; $params[]=$q; }
    // Pagination
    $perPage = min(100, max(10, (int)($d['per_page'] ?? 25)));
    $page = max(1, (int)($d['page'] ?? 1));
    $offset = ($page - 1) * $perPage;
    // Count total
    $countSql = 'SELECT COUNT(*) FROM bookings b WHERE '.implode(' AND ',$where);
    $cs = db()->prepare($countSql); $cs->execute($params);
    $total = (int)$cs->fetchColumn();
    // Page slice
    $sql = 'SELECT b.id,b.reference,b.guest_name,b.check_in,b.check_out,b.nights,b.total,b.currency,b.status,b.payment_status,b.guest_hash,p.name AS property_name FROM bookings b JOIN properties p ON p.id=b.property_id WHERE '.implode(' AND ',$where).' ORDER BY b.check_in DESC LIMIT '.$perPage.' OFFSET '.$offset;
    $stmt = db()->prepare($sql); $stmt->execute($params);
    json_ok([
        'rows' => $stmt->fetchAll(),
        'page' => $page,
        'per_page' => $perPage,
        'total' => $total,
        'total_pages' => (int)ceil($total / $perPage),
    ]);
}

csrf_check();

if ($method === 'POST') {
    require_permission('bookings.manage');
    $errors = v_required($d, ['property_id','guest_name','check_in','check_out']);
    if ($errors) json_error('Validation failed',422,$errors);
    $pid = (int)$d['property_id'];
    $in = v_date($d['check_in'], 'check_in');
    $out = v_date($d['check_out'], 'check_out');
    if (strtotime($out) <= strtotime($in)) json_error('Check-out must be after check-in',422);
    if (!empty($d['guest_email']) && !v_email((string)$d['guest_email'])) json_error('Invalid guest email',422);

    $pdo = db();
    $pdo->beginTransaction();
    try {
        $p = $pdo->prepare('SELECT id, base_price, cleaning_fee, currency, max_guests FROM properties WHERE id=? AND deleted_at IS NULL AND status="active" FOR UPDATE');
        $p->execute([$pid]);
        $prop = $p->fetch();
        if (!$prop) throw new Exception('Active property not found');

        $guests = max(1, (int)($d['guests_count'] ?? 1));
        if ($guests > (int)$prop['max_guests']) json_error('Guest count exceeds property capacity',422);

        $lockToken = null;
        if (function_exists('hv5_acquire_reservation_lock') && function_exists('hv5_table_exists') && hv5_table_exists('reservation_locks')) {
            $lockToken = hv5_acquire_reservation_lock($pdo, $pid, $in, $out, 'direct');
            if (!$lockToken) { $pdo->rollBack(); json_error('Property is not available for these dates', 409); }
        }

        $available = $lockToken ? true : (function_exists('hv4_availability_is_free')
            ? hv4_availability_is_free($pdo, $pid, $in, $out)
            : !(function () use ($pdo, $pid, $in, $out) {
                $c = $pdo->prepare("SELECT id FROM bookings WHERE property_id=? AND status NOT IN ('cancelled','no_show') AND NOT (check_out <= ? OR check_in >= ?) LIMIT 1 FOR UPDATE");
                $c->execute([$pid, $in, $out]);
                return (bool)$c->fetch();
            })());
        if (!$available) { $pdo->rollBack(); json_error('Property is not available for these dates',409); }

        $nights   = max(1, (int)((strtotime($out) - strtotime($in)) / 86400));
        $rate     = isset($d['nightly_rate']) && $d['nightly_rate'] !== '' ? v_positive_number($d['nightly_rate'], 'nightly_rate') : (float)$prop['base_price'];
        $subtotal = $rate * $nights;
        $cleaning = isset($d['cleaning_fee']) && $d['cleaning_fee'] !== '' ? max(0, (float)$d['cleaning_fee']) : (float)$prop['cleaning_fee'];
        $taxes    = max(0, (float)($d['taxes'] ?? 0));
        $total    = $subtotal + $cleaning + $taxes;
        $ref      = 'HV-' . strtoupper(bin2hex(random_bytes(4)));
        $status   = v_enum($d['status'] ?? null, $bookingStatuses, 'status', 'confirmed');

        // Generate unique guest portal hash
        $guestHash   = bin2hex(random_bytes(16));
        $guestUserId = !empty($d['guest_user_id']) ? (int)$d['guest_user_id'] : null;

        $ins = $pdo->prepare('INSERT INTO bookings (reference,property_id,guest_name,guest_email,guest_phone,check_in,check_out,guests_count,adults,children,nights,nightly_rate,subtotal,cleaning_fee,taxes,total,currency,status,source,notes,created_by,guest_user_id,guest_hash) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
        $ins->execute([
            $ref, $pid,
            v_str($d,'guest_name',150),
            v_str($d,'guest_email',190) ?: null,
            v_str($d,'guest_phone',40) ?: null,
            $in, $out,
            $guests,
            max(1, (int)($d['adults'] ?? $guests)),
            max(0, (int)($d['children'] ?? 0)),
            $nights, $rate, $subtotal, $cleaning, $taxes, $total,
            $prop['currency'],
            $status,
            v_str($d,'source',50) ?: 'direct',
            (string)($d['notes'] ?? ''),
            current_user()['id'] ?? null,
            $guestUserId,
            $guestHash,
        ]);
        $nid = (int)$pdo->lastInsertId();
        if ($lockToken && function_exists('hv5_commit_reservation_lock')) hv5_commit_reservation_lock($pdo, $lockToken, $nid);
        if (function_exists('hv4_sync_calendar_block')) hv4_sync_calendar_block($pdo, $nid);
        $pdo->commit();
        audit('booking.create','booking',$nid, $ref);
        if (function_exists('hv4_emit_event')) hv4_emit_event('booking.created', ['id'=>$nid,'reference'=>$ref,'property_id'=>$pid], 'booking', $nid);
        if (function_exists('hv4_queue_outbound_sync')) hv4_queue_outbound_sync('booking.created', ['booking_id'=>$nid,'property_id'=>$pid]);
        json_ok(['id'=>$nid,'reference'=>$ref,'total'=>$total,'guest_hash'=>$guestHash], 'Booking created');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        error_log('Booking Create Error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
        global $CONFIG;
        $msg = 'Booking failed. Please verify dates, capacity, and property status.';
        if (!empty($CONFIG['app']['debug'])) {
            $msg .= ' Error: ' . $e->getMessage();
        }
        json_error($msg, 500);
    }
}

if ($method === 'PUT') {
    require_permission('bookings.manage');
    if (!$id) json_error('id required',422);
    $allowed=['status','payment_status','notes','guest_email','guest_phone','guests_count','adults','children','guest_user_id'];
    $fields=[]; $vals=[];
    foreach ($allowed as $f) {
        if (array_key_exists($f,$d)) {
            if ($f === 'status') $d[$f] = v_enum($d[$f], $bookingStatuses, 'status');
            if ($f === 'payment_status') $d[$f] = v_enum($d[$f], $paymentStatuses, 'payment_status');
            if ($f === 'guest_email' && $d[$f] !== '' && !v_email((string)$d[$f])) json_error('Invalid guest email',422);
            if ($f === 'guest_user_id') $d[$f] = !empty($d[$f]) ? (int)$d[$f] : null;
            $fields[]="$f=?"; $vals[]=$d[$f];
        }
    }
    if (!$fields) json_error('Nothing to update',422);
    if (in_array('status=?',$fields, true) && ($d['status'] ?? '')==='cancelled') {
        $fields[]='cancelled_at=NOW()';
    }
    $vals[]=$id;
    db()->prepare('UPDATE bookings SET '.implode(',',$fields).' WHERE id=?')->execute($vals);
    audit('booking.update','booking',$id);
    json_ok([], 'Booking updated');
}

if ($method === 'DELETE') {
    require_permission('bookings.manage');
    if (!$id) json_error('id required',422);
    db()->prepare('UPDATE bookings SET status="cancelled", cancelled_at=NOW() WHERE id=?')->execute([$id]);
    audit('booking.cancel','booking',$id);
    json_ok([], 'Booking cancelled');
}

json_error('Method not allowed',405);
