package service

import (
	"aggios-app/backend/internal/config"
	"aggios-app/backend/internal/domain"
	"aggios-app/backend/internal/repository"
	"database/sql"
	"fmt"

	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
)

// AgencyService handles agency registration and management
type AgencyService struct {
	userRepo   *repository.UserRepository
	tenantRepo *repository.TenantRepository
	cfg        *config.Config
	db         *sql.DB
}

// NewAgencyService creates a new agency service
func NewAgencyService(userRepo *repository.UserRepository, tenantRepo *repository.TenantRepository, cfg *config.Config, db *sql.DB) *AgencyService {
	return &AgencyService{
		userRepo:   userRepo,
		tenantRepo: tenantRepo,
		cfg:        cfg,
		db:         db,
	}
}

// RegisterAgency creates a new agency (tenant) and its admin user
// Only SUPERADMIN can call this
func (s *AgencyService) RegisterAgency(req domain.RegisterAgencyRequest) (*domain.Tenant, *domain.User, error) {
	// Validate password
	if len(req.AdminPassword) < s.cfg.Security.PasswordMinLength {
		return nil, nil, ErrWeakPassword
	}

	// Check if subdomain is available
	exists, err := s.tenantRepo.SubdomainExists(req.Subdomain)
	if err != nil {
		return nil, nil, err
	}
	if exists {
		return nil, nil, ErrSubdomainTaken
	}

	// Check if admin email already exists
	emailExists, err := s.userRepo.EmailExists(req.AdminEmail)
	if err != nil {
		return nil, nil, err
	}
	if emailExists {
		return nil, nil, ErrEmailAlreadyExists
	}

	// Create tenant
	address := req.Street
	if req.Number != "" {
		address += ", " + req.Number
	}
	if req.Complement != "" {
		address += " - " + req.Complement
	}

	tenant := &domain.Tenant{
		Name:              req.AgencyName,
		Domain:            fmt.Sprintf("%s.%s", req.Subdomain, s.cfg.App.BaseDomain),
		Subdomain:         req.Subdomain,
		CNPJ:              req.CNPJ,
		RazaoSocial:       req.RazaoSocial,
		Email:             req.AdminEmail,
		Phone:             req.Phone,
		Website:           req.Website,
		Address:           address,
		Neighborhood:      req.Neighborhood,
		Number:            req.Number,
		Complement:        req.Complement,
		City:              req.City,
		State:             req.State,
		Zip:               req.CEP,
		Description:       req.Description,
		Industry:          req.Industry,
		TeamSize:          req.TeamSize,
		PrimaryColor:      req.PrimaryColor,
		SecondaryColor:    req.SecondaryColor,
		LogoURL:           req.LogoURL,
		LogoHorizontalURL: req.LogoHorizontalURL,
	}

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

	// Hash password
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.AdminPassword), bcrypt.DefaultCost)
	if err != nil {
		return nil, nil, err
	}

	// Create admin user for the agency
	adminUser := &domain.User{
		TenantID: &tenant.ID,
		Email:    req.AdminEmail,
		Password: string(hashedPassword),
		Name:     req.AdminName,
		Role:     "ADMIN_AGENCIA",
	}

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

	return tenant, adminUser, nil
}

// RegisterClient creates a new client user for a specific agency
// Only ADMIN_AGENCIA can call this
func (s *AgencyService) RegisterClient(req domain.RegisterClientRequest, tenantID uuid.UUID) (*domain.User, error) {
	// Validate password
	if len(req.Password) < s.cfg.Security.PasswordMinLength {
		return nil, ErrWeakPassword
	}

	// Check if email already exists
	exists, err := s.userRepo.EmailExists(req.Email)
	if err != nil {
		return nil, err
	}
	if exists {
		return nil, ErrEmailAlreadyExists
	}

	// Hash password
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		return nil, err
	}

	// Create client user
	client := &domain.User{
		TenantID: &tenantID,
		Email:    req.Email,
		Password: string(hashedPassword),
		Name:     req.Name,
		Role:     "CLIENTE",
	}

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

	return client, nil
}

// GetAgencyDetails returns tenant and admin information for superadmin view
func (s *AgencyService) GetAgencyDetails(id uuid.UUID) (*domain.AgencyDetails, error) {
	tenant, err := s.tenantRepo.FindByID(id)
	if err != nil {
		return nil, err
	}
	if tenant == nil {
		return nil, ErrTenantNotFound
	}

	admin, err := s.userRepo.FindAdminByTenantID(id)
	if err != nil {
		return nil, err
	}

	protocol := "http://"
	if s.cfg.App.Environment == "production" {
		protocol = "https://"
	}

	details := &domain.AgencyDetails{
		Tenant:    tenant,
		AccessURL: fmt.Sprintf("%s%s", protocol, tenant.Domain),
	}

	if admin != nil {
		details.Admin = admin
	}

	// Buscar subscription e soluções
	var subscription domain.AgencySubscriptionInfo
	query := `
		SELECT 
			s.plan_id, 
			p.name as plan_name,
			s.status
		FROM agency_subscriptions s
		JOIN plans p ON s.plan_id = p.id
		WHERE s.agency_id = $1
		LIMIT 1
	`
	err = s.db.QueryRow(query, id).Scan(&subscription.PlanID, &subscription.PlanName, &subscription.Status)
	if err == nil {
		// Buscar soluções do plano
		solutionsQuery := `
			SELECT sol.id, sol.name, sol.slug, sol.icon
			FROM solutions sol
			JOIN plan_solutions ps ON sol.id = ps.solution_id
			WHERE ps.plan_id = $1
			ORDER BY sol.name
		`
		rows, err := s.db.Query(solutionsQuery, subscription.PlanID)
		if err == nil {
			defer rows.Close()
			var solutions []domain.Solution
			for rows.Next() {
				var solution domain.Solution
				if err := rows.Scan(&solution.ID, &solution.Name, &solution.Slug, &solution.Icon); err == nil {
					solutions = append(solutions, solution)
				}
			}
			subscription.Solutions = solutions
			details.Subscription = &subscription
		}
	}

	return details, nil
}

// DeleteAgency removes a tenant and its related resources
func (s *AgencyService) DeleteAgency(id uuid.UUID) error {
	tenant, err := s.tenantRepo.FindByID(id)
	if err != nil {
		return err
	}
	if tenant == nil {
		return ErrTenantNotFound
	}

	return s.tenantRepo.Delete(id)
}

// UpdateAgencyStatus updates the is_active status of a tenant
func (s *AgencyService) UpdateAgencyStatus(id uuid.UUID, isActive bool) error {
	tenant, err := s.tenantRepo.FindByID(id)
	if err != nil {
		return err
	}
	if tenant == nil {
		return ErrTenantNotFound
	}

	return s.tenantRepo.UpdateStatus(id, isActive)
}
