package bookings

import (
	"context"
	"errors"
	"time"

	"bookra/apps/backend/internal/db"
	"bookra/apps/backend/internal/domain"
	"bookra/apps/backend/internal/notifications"
)

var (
	ErrInvalidReschedule = errors.New("invalid reschedule request")
	ErrBookingCancelled  = errors.New("booking already cancelled")
	ErrUnauthorized      = errors.New("unauthorized access")
)

type CustomerService struct {
	repo     db.Repository
	notifier CustomerNotifier
}

type CustomerNotifier interface {
	SendBookingReschedule(ctx context.Context, data notifications.BookingEmailData) error
	SendBookingCancellation(ctx context.Context, data notifications.BookingEmailData) error
}

func NewCustomerService(repo db.Repository, notifier CustomerNotifier) *CustomerService {
	if notifier == nil {
		notifier = &customerNoopNotifier{}
	}
	return &CustomerService{repo: repo, notifier: notifier}
}

type customerNoopNotifier struct{}

func (n *customerNoopNotifier) SendBookingReschedule(ctx context.Context, data notifications.BookingEmailData) error {
	return nil
}

func (n *customerNoopNotifier) SendBookingCancellation(ctx context.Context, data notifications.BookingEmailData) error {
	return nil
}

// GetBookingByReference returns booking details for customer management link
func (s *CustomerService) GetBookingByReference(ctx context.Context, reference string, token string) (domain.CustomerBookingView, error) {
	booking, err := s.repo.GetBookingByReference(ctx, reference)
	if err != nil {
		return domain.CustomerBookingView{}, ErrBookingNotFound
	}

	// Get tenant details for business name
	tenant, err := s.repo.GetTenantByID(ctx, booking.TenantID)
	if err != nil {
		return domain.CustomerBookingView{}, err
	}

	return domain.CustomerBookingView{
		Reference:     booking.Reference,
		CustomerName:  booking.CustomerName,
		CustomerEmail: booking.CustomerEmail,
		Service:       "Service", // Would get from service ID in full implementation
		BusinessName:  tenant.Name,
		StartsAt:      booking.StartsAt,
		EndsAt:        booking.EndsAt,
		Location:      "Location", // Would get from location ID in full implementation
		Status:        booking.Status,
	}, nil
}

// RescheduleBooking allows customers to reschedule their booking
func (s *CustomerService) RescheduleBooking(ctx context.Context, reference string, req domain.RescheduleBookingRequest, token string) error {
	booking, err := s.repo.GetBookingByReference(ctx, reference)
	if err != nil {
		return ErrBookingNotFound
	}

	if booking.Status == "cancelled" {
		return ErrBookingCancelled
	}

	newStartsAt, err := time.Parse(time.RFC3339, req.NewStartsAt)
	if err != nil {
		return ErrInvalidReschedule
	}

	newEndsAt, err := time.Parse(time.RFC3339, req.NewEndsAt)
	if err != nil {
		return ErrInvalidReschedule
	}

	if !newEndsAt.After(newStartsAt) {
		return ErrInvalidReschedule
	}

	if newStartsAt.Before(time.Now().UTC()) {
		return ErrInvalidReschedule
	}

	if err := s.repo.RescheduleBooking(ctx, booking.ID, newStartsAt, newEndsAt); err != nil {
		return err
	}

	// Send reschedule email (async)
	go s.sendRescheduleEmail(booking, newStartsAt, newEndsAt)

	return nil
}

// CancelBooking allows customers to cancel their booking
func (s *CustomerService) CancelBooking(ctx context.Context, reference string, token string) error {
	booking, err := s.repo.GetBookingByReference(ctx, reference)
	if err != nil {
		return ErrBookingNotFound
	}

	if booking.Status == "cancelled" {
		return ErrBookingCancelled
	}

	if err := s.repo.UpdateBookingStatus(ctx, booking.ID, "cancelled"); err != nil {
		return err
	}

	// Send cancellation email (async)
	go s.sendCancellationEmail(booking)

	return nil
}

func (s *CustomerService) sendRescheduleEmail(booking db.BookingRecord, newStartsAt, newEndsAt time.Time) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	tenant, err := s.repo.GetTenantByID(ctx, booking.TenantID)
	if err != nil {
		return
	}

	brand, _ := s.repo.GetBrandProfile(ctx, tenant.ID)
	brandColor := brand.PrimaryColor
	if brandColor == "" {
		brandColor = "#a65c3e"
	}

	managementURL := "https://bookra.eu/manage/" + booking.Reference + "?token=" + booking.Reference

	emailData := notifications.BookingEmailData{
		TenantName:    tenant.Name,
		TenantSlug:    tenant.Slug,
		BrandColor:    brandColor,
		CustomerName:  booking.CustomerName,
		CustomerEmail: booking.CustomerEmail,
		Service:       "Service",
		Location:      "Location",
		Reference:     booking.Reference,
		StartsAt:      newStartsAt,
		EndsAt:        newEndsAt,
		Timezone:      tenant.Timezone,
		Locale:        tenant.Locale,
		ManagementURL: managementURL,
	}

	s.notifier.SendBookingReschedule(ctx, emailData)
}

func (s *CustomerService) sendCancellationEmail(booking db.BookingRecord) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	tenant, err := s.repo.GetTenantByID(ctx, booking.TenantID)
	if err != nil {
		return
	}

	brand, _ := s.repo.GetBrandProfile(ctx, tenant.ID)
	brandColor := brand.PrimaryColor
	if brandColor == "" {
		brandColor = "#a65c3e"
	}

	emailData := notifications.BookingEmailData{
		TenantName:    tenant.Name,
		TenantSlug:    tenant.Slug,
		BrandColor:    brandColor,
		CustomerName:  booking.CustomerName,
		CustomerEmail: booking.CustomerEmail,
		Service:       "Service",
		Location:      "Location",
		Reference:     booking.Reference,
		StartsAt:      booking.StartsAt,
		EndsAt:        booking.EndsAt,
		Timezone:      tenant.Timezone,
		Locale:        tenant.Locale,
		ManagementURL: "",
	}

	s.notifier.SendBookingCancellation(ctx, emailData)
}
