package service

import (
	"database/sql"
	"errors"
	"fmt"

	"aggios-app/backend/internal/domain"
	"aggios-app/backend/internal/repository"

	"github.com/google/uuid"
	"github.com/shopspring/decimal"
)

var (
	ErrPlanNotFound           = errors.New("plan not found")
	ErrPlanSlugTaken          = errors.New("plan slug already exists")
	ErrInvalidUserRange       = errors.New("invalid user range: min_users must be less than or equal to max_users")
	ErrSubscriptionNotFound   = errors.New("subscription not found")
	ErrUserLimitExceeded      = errors.New("user limit exceeded for this plan")
	ErrSubscriptionExists     = errors.New("agency already has an active subscription")
)

// PlanService handles plan business logic
type PlanService struct {
	planRepo         *repository.PlanRepository
	subscriptionRepo *repository.SubscriptionRepository
}

// NewPlanService creates a new plan service
func NewPlanService(planRepo *repository.PlanRepository, subscriptionRepo *repository.SubscriptionRepository) *PlanService {
	return &PlanService{
		planRepo:         planRepo,
		subscriptionRepo: subscriptionRepo,
	}
}

// CreatePlan creates a new plan
func (s *PlanService) CreatePlan(req *domain.CreatePlanRequest) (*domain.Plan, error) {
	// Validate user range
	if req.MinUsers > req.MaxUsers && req.MaxUsers != -1 {
		return nil, ErrInvalidUserRange
	}

	// Check if slug is unique
	existing, _ := s.planRepo.GetBySlug(req.Slug)
	if existing != nil {
		return nil, ErrPlanSlugTaken
	}

	plan := &domain.Plan{
		Name:            req.Name,
		Slug:            req.Slug,
		Description:     req.Description,
		MinUsers:        req.MinUsers,
		MaxUsers:        req.MaxUsers,
		Features:        req.Features,
		Differentiators: req.Differentiators,
		StorageGB:       req.StorageGB,
		IsActive:        req.IsActive,
	}

	// Convert prices if provided
	if req.MonthlyPrice != nil {
		price := decimal.NewFromFloat(*req.MonthlyPrice)
		plan.MonthlyPrice = &price
	}
	if req.AnnualPrice != nil {
		price := decimal.NewFromFloat(*req.AnnualPrice)
		plan.AnnualPrice = &price
	}

	if err := s.planRepo.Create(plan); err != nil {
		return nil, err
	}

	return plan, nil
}

// GetPlan retrieves a plan by ID
func (s *PlanService) GetPlan(id uuid.UUID) (*domain.Plan, error) {
	plan, err := s.planRepo.GetByID(id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, ErrPlanNotFound
		}
		return nil, err
	}
	return plan, nil
}

// ListPlans retrieves all plans
func (s *PlanService) ListPlans() ([]*domain.Plan, error) {
	return s.planRepo.ListAll()
}

// ListActivePlans retrieves all active plans
func (s *PlanService) ListActivePlans() ([]*domain.Plan, error) {
	return s.planRepo.ListActive()
}

// UpdatePlan updates a plan
func (s *PlanService) UpdatePlan(id uuid.UUID, req *domain.UpdatePlanRequest) (*domain.Plan, error) {
	plan, err := s.planRepo.GetByID(id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, ErrPlanNotFound
		}
		return nil, err
	}

	// Update fields if provided
	if req.Name != nil {
		plan.Name = *req.Name
	}
	if req.Slug != nil {
		// Check if new slug is unique
		existing, _ := s.planRepo.GetBySlug(*req.Slug)
		if existing != nil && existing.ID != plan.ID {
			return nil, ErrPlanSlugTaken
		}
		plan.Slug = *req.Slug
	}
	if req.Description != nil {
		plan.Description = *req.Description
	}
	if req.MinUsers != nil {
		plan.MinUsers = *req.MinUsers
	}
	if req.MaxUsers != nil {
		plan.MaxUsers = *req.MaxUsers
	}
	if req.MonthlyPrice != nil {
		price := decimal.NewFromFloat(*req.MonthlyPrice)
		plan.MonthlyPrice = &price
	}
	if req.AnnualPrice != nil {
		price := decimal.NewFromFloat(*req.AnnualPrice)
		plan.AnnualPrice = &price
	}
	if req.Features != nil {
		plan.Features = req.Features
	}
	if req.Differentiators != nil {
		plan.Differentiators = req.Differentiators
	}
	if req.StorageGB != nil {
		plan.StorageGB = *req.StorageGB
	}
	if req.IsActive != nil {
		plan.IsActive = *req.IsActive
	}

	// Validate user range
	if plan.MinUsers > plan.MaxUsers && plan.MaxUsers != -1 {
		return nil, ErrInvalidUserRange
	}

	if err := s.planRepo.Update(plan); err != nil {
		return nil, err
	}

	return plan, nil
}

// DeletePlan deletes a plan
func (s *PlanService) DeletePlan(id uuid.UUID) error {
	// Check if plan exists
	if _, err := s.planRepo.GetByID(id); err != nil {
		if err == sql.ErrNoRows {
			return ErrPlanNotFound
		}
		return err
	}

	return s.planRepo.Delete(id)
}

// CreateSubscription creates a new subscription for an agency
func (s *PlanService) CreateSubscription(req *domain.CreateSubscriptionRequest) (*domain.Subscription, error) {
	// Check if plan exists
	plan, err := s.planRepo.GetByID(req.PlanID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, ErrPlanNotFound
		}
		return nil, err
	}

	// Check if agency already has active subscription
	existing, err := s.subscriptionRepo.GetByAgencyID(req.AgencyID)
	if err != nil && err != sql.ErrNoRows {
		return nil, err
	}
	if existing != nil {
		return nil, ErrSubscriptionExists
	}

	subscription := &domain.Subscription{
		AgencyID:    req.AgencyID,
		PlanID:      req.PlanID,
		BillingType: req.BillingType,
		Status:      "active",
		CurrentUsers: 0,
	}

	if err := s.subscriptionRepo.Create(subscription); err != nil {
		return nil, err
	}

	// Load plan details
	subscription.PlanID = plan.ID

	return subscription, nil
}

// GetSubscription retrieves a subscription by ID
func (s *PlanService) GetSubscription(id uuid.UUID) (*domain.Subscription, error) {
	subscription, err := s.subscriptionRepo.GetByID(id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, ErrSubscriptionNotFound
		}
		return nil, err
	}
	return subscription, nil
}

// GetAgencySubscription retrieves an agency's active subscription
func (s *PlanService) GetAgencySubscription(agencyID uuid.UUID) (*domain.Subscription, error) {
	subscription, err := s.subscriptionRepo.GetByAgencyID(agencyID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, ErrSubscriptionNotFound
		}
		return nil, err
	}
	return subscription, nil
}

// ListSubscriptions retrieves all subscriptions
func (s *PlanService) ListSubscriptions() ([]*domain.Subscription, error) {
	return s.subscriptionRepo.ListAll()
}

// ValidateUserLimit checks if adding a user would exceed plan limit
func (s *PlanService) ValidateUserLimit(agencyID uuid.UUID, newUserCount int) error {
	subscription, err := s.subscriptionRepo.GetByAgencyID(agencyID)
	if err != nil {
		if err == sql.ErrNoRows {
			return ErrSubscriptionNotFound
		}
		return err
	}

	plan, err := s.planRepo.GetByID(subscription.PlanID)
	if err != nil {
		if err == sql.ErrNoRows {
			return ErrPlanNotFound
		}
		return err
	}

	if plan.MaxUsers != -1 && newUserCount > plan.MaxUsers {
		return fmt.Errorf("%w (limit: %d, requested: %d)", ErrUserLimitExceeded, plan.MaxUsers, newUserCount)
	}

	return nil
}

// GetPlanByUserCount returns the appropriate plan for a given user count
func (s *PlanService) GetPlanByUserCount(userCount int) (*domain.Plan, error) {
	plans, err := s.planRepo.ListActive()
	if err != nil {
		return nil, err
	}

	// Find the plan that fits the user count
	for _, plan := range plans {
		if userCount >= plan.MinUsers && (plan.MaxUsers == -1 || userCount <= plan.MaxUsers) {
			return plan, nil
		}
	}

	return nil, fmt.Errorf("no plan found for user count: %d", userCount)
}
