package company

import (
	"context"
	"errors"

	"tm/internal/company_category"
	"tm/pkg/logger"
	"tm/pkg/response"
)

// Service defines business logic for company operations
type Service interface {
	// Core company operations
	Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error)
	Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error)
	GetByID(ctx context.Context, id string) (*CompanyResponse, error)
	GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error)
	Delete(ctx context.Context, id string) error

	// Company listing and search
	SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error)

	// Company management
	UpdateStatus(ctx context.Context, id string, form *StatusForm) error
	UpdateVerification(ctx context.Context, id string, form *VerificationForm) error

	// Get my company
	GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error)
}

// companyService implements the Service interface
type companyService struct {
	repository      Repository
	categoryService company_category.Service
	logger          logger.Logger
}

// NewService creates a new company service
func NewService(repository Repository, categoryService company_category.Service, logger logger.Logger) Service {
	return &companyService{
		repository:      repository,
		categoryService: categoryService,
		logger:          logger,
	}
}

// Create creates a new company
func (s *companyService) Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error) {
	// Validate categories if provided
	if form.Tags != nil && len(form.Tags.Categories) > 0 {
		validCategories, err := s.categoryService.GetByIDs(ctx, form.Tags.Categories)
		if err != nil {
			s.logger.Error("Failed to validate categories", map[string]interface{}{
				"error":      err.Error(),
				"categories": form.Tags.Categories,
			})
			return nil, errors.New("failed to validate categories")
		}

		// Check if all provided categories exist
		if len(validCategories) != len(form.Tags.Categories) {
			return nil, errors.New("one or more categories are invalid")
		}
	}

	// Check if company name already exists
	existingCompany, _ := s.repository.GetByName(ctx, form.Name)
	if existingCompany != nil {
		return nil, errors.New("company with this name already exists")
	}

	// Check if registration number already exists
	existingCompany, _ = s.repository.GetByRegistrationNumber(ctx, form.RegistrationNumber)
	if existingCompany != nil {
		return nil, errors.New("company with this registration number already exists")
	}

	// Check if tax ID already exists
	existingCompany, _ = s.repository.GetByTaxID(ctx, form.TaxID)
	if existingCompany != nil {
		return nil, errors.New("company with this tax ID already exists")
	}

	// Create company
	company := &Company{
		Name:               form.Name,
		Type:               CompanyType(form.Type),
		Status:             CompanyStatusActive,
		RegistrationNumber: form.RegistrationNumber,
		TaxID:              form.TaxID,
		Industry:           form.Industry,
		Description:        form.Description,
		Website:            form.Website,
		EmployeeCount:      form.EmployeeCount,
		AnnualRevenue:      form.AnnualRevenue,
		FoundedYear:        form.FoundedYear,
		Address:            s.convertAddressForm(form.Address),
		Phone:              form.Phone,
		Email:              form.Email,
		Tags:               s.convertCompanyTagsForm(form.Tags),
		IsVerified:         false,
		IsCompliant:        false,
		Language:           s.getDefaultLanguage(form.Language),
		Currency:           s.getDefaultCurrency(form.Currency),
		Timezone:           s.getDefaultTimezone(form.Timezone),
	}

	// Save to database
	err := s.repository.Create(ctx, company)
	if err != nil {
		s.logger.Error("Failed to create company", map[string]interface{}{
			"error":               err.Error(),
			"name":                form.Name,
			"registration_number": form.RegistrationNumber,
		})
		return nil, err
	}

	s.logger.Info("Company created successfully", map[string]interface{}{
		"company_id": company.ID,
		"name":       company.Name,
		"type":       company.Type,
	})

	return company.ToResponse(nil), nil
}

// GetCompanyByID retrieves a company by ID
func (s *companyService) GetByID(ctx context.Context, id string) (*CompanyResponse, error) {
	company, err := s.repository.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to get company by ID", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return nil, err
	}

	s.logger.Info("Company retrieved successfully", map[string]interface{}{
		"company_id": company.ID,
		"name":       company.Name,
	})

	// Get category details for response
	categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
	if err != nil {
		s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
			"error":      err.Error(),
			"company_id": company.ID,
		})
	}

	var convertedCategories []*CategoryResponse
	for _, cat := range categoryDetails {
		convertedCategories = append(convertedCategories, &CategoryResponse{
			ID:   cat.ID,
			Name: cat.Name,
		})
	}

	return company.ToResponse(convertedCategories), nil
}

// UpdateCompany updates a company
func (s *companyService) Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error) {
	// Validate categories if provided
	if form.Tags != nil && len(form.Tags.Categories) > 0 {
		validCategories, err := s.categoryService.GetByIDs(ctx, form.Tags.Categories)
		if err != nil {
			s.logger.Error("Failed to validate categories", map[string]interface{}{
				"error":      err.Error(),
				"categories": form.Tags.Categories,
			})
			return nil, errors.New("failed to validate categories")
		}

		// Check if all provided categories exist
		if len(validCategories) != len(form.Tags.Categories) {
			return nil, errors.New("one or more categories are invalid")
		}
	}

	// Get existing company
	company, err := s.repository.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to get company for update", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return nil, errors.New("company not found")
	}

	// Check if name already exists (if changing name)
	if form.Name != "" && form.Name != company.Name {
		existingCompany, _ := s.repository.GetByName(ctx, form.Name)
		if existingCompany != nil {
			return nil, errors.New("company with this name already exists")
		}
		company.Name = form.Name
	}

	// Check if registration number already exists (if changing)
	if form.RegistrationNumber != "" && form.RegistrationNumber != company.RegistrationNumber {
		existingCompany, _ := s.repository.GetByRegistrationNumber(ctx, form.RegistrationNumber)
		if existingCompany != nil {
			return nil, errors.New("company with this registration number already exists")
		}
		company.RegistrationNumber = form.RegistrationNumber
	}

	// Check if tax ID already exists (if changing)
	if form.TaxID != "" && form.TaxID != company.TaxID {
		existingCompany, _ := s.repository.GetByTaxID(ctx, form.TaxID)
		if existingCompany != nil {
			return nil, errors.New("company with this tax ID already exists")
		}
		company.TaxID = form.TaxID
	}

	// Update fields if provided
	if form.Type != "" {
		company.Type = CompanyType(form.Type)
	}

	if form.Industry != "" {
		company.Industry = form.Industry
	}

	if form.Description != nil {
		company.Description = form.Description
	}

	if form.Website != nil {
		company.Website = form.Website
	}

	if form.EmployeeCount != nil {
		company.EmployeeCount = form.EmployeeCount
	}

	if form.AnnualRevenue != nil {
		company.AnnualRevenue = form.AnnualRevenue
	}

	if form.FoundedYear != nil {
		company.FoundedYear = form.FoundedYear
	}

	if form.Address != nil {
		company.Address = s.convertAddressForm(form.Address)
	}

	if form.Phone != nil {
		company.Phone = form.Phone
	}

	if form.Email != nil {
		company.Email = form.Email
	}

	if form.Tags != nil {
		company.Tags = s.convertCompanyTagsForm(form.Tags)
	}

	if form.Language != nil {
		company.Language = *form.Language
	}

	if form.Currency != nil {
		company.Currency = *form.Currency
	}

	if form.Timezone != nil {
		company.Timezone = *form.Timezone
	}

	// Update in database
	err = s.repository.Update(ctx, company)
	if err != nil {
		s.logger.Error("Failed to update company", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return nil, errors.New("failed to update company")
	}

	s.logger.Info("Company updated successfully", map[string]interface{}{
		"company_id": company.ID,
		"name":       company.Name,
	})

	return company.ToResponse(nil), nil
}

// DeleteCompany deletes a company (soft delete)
func (s *companyService) Delete(ctx context.Context, id string) error {
	// Delete company (soft delete)
	err := s.repository.Delete(ctx, id)
	if err != nil {
		s.logger.Error("Failed to delete company", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return errors.New("failed to delete company")
	}

	s.logger.Info("Company deleted successfully", map[string]interface{}{
		"company_id": id,
	})

	return nil
}

// SearchCompanies searches companies with advanced filters
func (s *companyService) SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error) {

	// Get companies using search
	companies, total, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to search companies", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("failed to search companies")
	}

	// Convert to responses
	var companyResponses []*CompanyResponse
	for _, company := range companies {
		// Get category details for response
		categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
		if err != nil {
			s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
				"error":      err.Error(),
				"company_id": company.ID,
			})
		}

		var convertedCategories []*CategoryResponse
		for _, cat := range categoryDetails {
			convertedCategories = append(convertedCategories, &CategoryResponse{
				ID:   cat.ID,
				Name: cat.Name,
			})
		}

		companyResponses = append(companyResponses, company.ToResponse(convertedCategories))
	}

	return &CompanyListResponse{
		Companies: companyResponses,
		Meta:      pagination.Response(total),
	}, nil
}

// GetByIDs retrieves companies by IDs
func (s *companyService) GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error) {
	companies, err := s.repository.GetByIDs(ctx, ids)
	if err != nil {
		return nil, errors.New("failed to get companies by IDs")
	}

	var companyResponses []*CompanyResponse
	for _, company := range companies {
		// Get category details for response
		categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
		if err != nil {
			s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
				"error":      err.Error(),
				"company_id": company.ID,
			})
		}

		var convertedCategories []*CategoryResponse
		for _, cat := range categoryDetails {
			convertedCategories = append(convertedCategories, &CategoryResponse{
				ID:   cat.ID,
				Name: cat.Name,
			})
		}

		companyResponses = append(companyResponses, company.ToResponse(convertedCategories))
	}

	return companyResponses, nil
}

// UpdateCompanyStatus updates company status
func (s *companyService) UpdateStatus(ctx context.Context, id string, form *StatusForm) error {
	// Get company to check if exists
	_, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return errors.New("company not found")
	}

	// Update status
	err = s.repository.UpdateStatus(ctx, id, CompanyStatus(form.Status))
	if err != nil {
		s.logger.Error("Failed to update company status", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
			"status":     CompanyStatus(form.Status),
		})
		return errors.New("failed to update company status")
	}

	s.logger.Info("Company status updated successfully", map[string]interface{}{
		"company_id": id,
		"status":     form.Status,
	})

	return nil
}

// UpdateCompanyVerification updates company verification status
func (s *companyService) UpdateVerification(ctx context.Context, id string, form *VerificationForm) error {
	// Get company to check if exists
	_, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return errors.New("company not found")
	}

	// Update verification
	err = s.repository.UpdateVerification(ctx, id, form.IsVerified, form.IsCompliant, form.ComplianceNotes)
	if err != nil {
		s.logger.Error("Failed to update company verification", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return errors.New("failed to update company verification")
	}

	s.logger.Info("Company verification updated successfully", map[string]interface{}{
		"company_id":   id,
		"is_verified":  form.IsVerified,
		"is_compliant": form.IsCompliant,
	})

	return nil
}

// GetMyCompany retrieves the company for the authenticated user
func (s *companyService) GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error) {
	company, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return nil, errors.New("company not found")
	}

	return company.ToProfileResponse(), nil
}

// Helper methods for converting forms to domain objects
func (s *companyService) convertAddressForm(form *AddressForm) *Address {
	if form == nil {
		return nil
	}

	return &Address{
		Street:     form.Street,
		City:       form.City,
		State:      form.State,
		PostalCode: form.PostalCode,
		Country:    form.Country,
	}
}

func (s *companyService) convertCompanyTagsForm(form *CompanyTagsForm) *CompanyTags {
	if form == nil {
		return nil
	}

	return &CompanyTags{
		CPVCodes:        form.CPVCodes,
		Categories:      form.Categories,
		Keywords:        form.Keywords,
		Specializations: form.Specializations,
		Certifications:  form.Certifications,
	}
}

func (s *companyService) getDefaultLanguage(language *string) string {
	if language != nil {
		return *language
	}
	return "en"
}

func (s *companyService) getDefaultCurrency(currency *string) string {
	if currency != nil {
		return *currency
	}
	return "USD"
}

func (s *companyService) getDefaultTimezone(timezone *string) string {
	if timezone != nil {
		return *timezone
	}
	return "UTC"
}
