package controllers

import (
	"encoding/json"
	"fmt"
	"time"

	"doormile/constants"
	"doormile/db"
	"doormile/internal/notify"
	"doormile/models"
	"doormile/utils"
)

// AssignMilerToBooking is the single source of truth for manually assigning a
// miler to a pickup booking — shared by the admin console (AdminAssignMiler)
// and the hub console (HubAssignMiler) so both go through identical DB
// updates, NATS publish, and FCM notify instead of duplicating the logic.
func AssignMilerToBooking(bookingID, milerUserID int, assignedByUserID *int) (*models.PickupBooking, error) {
	tx := db.DB.Begin()

	var booking models.PickupBooking
	if err := tx.First(&booking, bookingID).Error; err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("booking not found")
	}

	booking.Status = constants.BookingMilerAssigned
	booking.Assignedmileruserid = &milerUserID
	booking.Updatedat = time.Now()
	if err := tx.Save(&booking).Error; err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("failed to update booking: %w", err)
	}

	assignment := models.BookingAssignment{
		Bookingid:        booking.Bookingid,
		Mileruserid:      milerUserID,
		Assignedbyuserid: assignedByUserID,
		Assignmentstatus: constants.AssignmentAssigned,
	}
	if err := tx.Create(&assignment).Error; err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("failed to create assignment: %w", err)
	}

	if err := tx.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).
		Update("availabilitystatus", constants.MilerAssigned).Error; err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("failed to update miler availability: %w", err)
	}

	tx.Commit()

	if db.Js != nil {
		payload := map[string]interface{}{
			"booking_id": booking.Bookingid,
			"booking_no": booking.Bookingno,
			"status":     booking.Status,
			"miler_id":   milerUserID,
			"updated_at": time.Now().UnixMilli(),
		}
		if data, err := json.Marshal(payload); err == nil {
			if _, err := db.Js.Publish("api.v1.bookings.update", data); err != nil {
				utils.Warn("Failed to publish booking.update to NATS", "booking_id", booking.Bookingid, "error", err)
			}
		}
	}

	var miler models.MilerProfile
	if db.DB.Where("userid = ?", milerUserID).First(&miler).Error == nil && miler.Devicetoken != "" {
		if err := notify.SendToDevice(
			miler.Devicetoken,
			"New Pickup Assigned",
			"New booking assigned — tap to view details",
			map[string]string{"booking_id": fmt.Sprintf("%d", booking.Bookingid)},
		); err != nil {
			utils.Warn("FCM: failed to notify miler on manual assignment",
				"miler_id", milerUserID, "booking_id", booking.Bookingid, "error", err)
		}
	}

	return &booking, nil
}
