package tender

import (
	"context"
	"fmt"
	"time"
	"tm/internal/company"
	"tm/pkg/logger"
	"tm/pkg/response"
)

// TenderService interface defines tender business logic operations
type Service interface {
	// Tender operations
	GetByID(ctx context.Context, id string) (*TenderResponse, error)
	Update(ctx context.Context, req UpdateTenderRequest) (*TenderResponse, error)
	Delete(ctx context.Context, id string) error
	Search(ctx context.Context, req *SearchForm, pagination *response.Pagination) (*SearchResponse, error)
	Recommend(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*SearchResponse, error)

	// Maintenance operations
	UpdateExpiredTenders(ctx context.Context) error
	CleanupOldData(ctx context.Context, olderThan time.Duration, pagination *response.Pagination) error
}

// tenderService implements TenderService interface
type tenderService struct {
	repository     TenderRepository
	companyService company.Service
	logger         logger.Logger
}

// NewService creates a new tender service
func NewService(repository TenderRepository, companyService company.Service, logger logger.Logger) Service {
	return &tenderService{
		repository:     repository,
		companyService: companyService,
		logger:         logger,
	}
}

// GetTenderByID retrieves a tender by ID
func (s *tenderService) GetByID(ctx context.Context, id string) (*TenderResponse, error) {
	s.logger.Info("Retrieving tender by ID", map[string]interface{}{
		"tender_id": id,
	})

	tender, err := s.repository.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to retrieve tender", map[string]interface{}{
			"tender_id": id,
			"error":     err.Error(),
		})
		return nil, fmt.Errorf("failed to retrieve tender: %w", err)
	}

	return tender.ToResponse(), nil
}

// UpdateTender updates an existing tender
func (s *tenderService) Update(ctx context.Context, req UpdateTenderRequest) (*TenderResponse, error) {
	s.logger.Info("Updating tender", map[string]interface{}{
		"tender_id": req.ID,
	})

	// Get existing tender
	tender, err := s.repository.GetByID(ctx, req.ID)
	if err != nil {
		s.logger.Error("Failed to find tender for update", map[string]interface{}{
			"tender_id": req.ID,
			"error":     err.Error(),
		})
		return nil, fmt.Errorf("failed to find tender: %w", err)
	}

	// Update fields if provided
	if req.Title != nil {
		tender.Title = *req.Title
	}
	if req.Description != nil {
		tender.Description = *req.Description
	}
	if req.Status != nil {
		tender.Status = *req.Status
	}
	if req.EstimatedValue != nil {
		tender.EstimatedValue = *req.EstimatedValue
	}
	if req.Currency != nil {
		tender.Currency = *req.Currency
	}
	if req.TenderDeadline != nil {
		tender.TenderDeadline = *req.TenderDeadline
	}

	if err := s.repository.Update(ctx, tender); err != nil {
		s.logger.Error("Failed to update tender", map[string]interface{}{
			"tender_id": req.ID,
			"error":     err.Error(),
		})
		return nil, fmt.Errorf("failed to update tender: %w", err)
	}

	s.logger.Info("Tender updated successfully", map[string]interface{}{
		"tender_id": tender.ID,
	})

	return tender.ToResponse(), nil
}

// Delete deletes a tender
func (s *tenderService) Delete(ctx context.Context, id string) error {
	s.logger.Info("Deleting tender", map[string]interface{}{
		"tender_id": id,
	})

	if err := s.repository.Delete(ctx, id); err != nil {
		s.logger.Error("Failed to delete tender", map[string]interface{}{
			"tender_id": id,
			"error":     err.Error(),
		})
		return fmt.Errorf("failed to delete tender: %w", err)
	}

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

	return nil
}

// ListTenders retrieves tenders with pagination and filtering
func (s *tenderService) Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*SearchResponse, error) {
	s.logger.Info("Listing tenders", map[string]interface{}{
		"limit":      pagination.Limit,
		"offset":     pagination.Offset,
		"company_id": form.CompanyID,
		"from":       form.DeadlineFrom,
		"to":         form.DeadlineTo,
	})

	tenders, total, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to list tenders", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, fmt.Errorf("failed to list tenders: %w", err)
	}

	// If no company ID provided, return tenders without match percentage
	if form.CompanyID == nil || *form.CompanyID == "" {
		tenderResponses := make([]TenderResponse, 0, len(tenders))
		for _, tender := range tenders {
			tenderResponses = append(tenderResponses, *tender.ToResponse())
		}

		return &SearchResponse{
			Tenders:  tenderResponses,
			Metadata: pagination.Response(total),
		}, nil
	}

	// Get company CPV codes for match calculation
	comp, err := s.companyService.GetByID(ctx, *form.CompanyID)
	if err != nil {
		s.logger.Error("Failed to get company for match calculation", map[string]interface{}{
			"company_id": *form.CompanyID,
			"error":      err.Error(),
		})
		// Continue without match percentage if company not found
		tenderResponses := make([]TenderResponse, 0, len(tenders))
		for _, tender := range tenders {
			tenderResponses = append(tenderResponses, *tender.ToResponse())
		}

		return &SearchResponse{
				Tenders:  tenderResponses,
				Metadata: pagination.Response(total),
			},
			nil
	}

	var companyCPVCodes []string
	if comp.Tags != nil && comp.Tags.CPVCodes != nil {
		companyCPVCodes = comp.Tags.CPVCodes
	}

	s.logger.Info("Tenders sorted by match percentage", map[string]interface{}{
		"company_id":        *form.CompanyID,
		"company_cpv_codes": len(companyCPVCodes),
		"total_tenders":     len(tenders),
	})

	tenderResponses := make([]TenderResponse, 0, len(tenders))
	for _, tender := range tenders {
		tenderResponses = append(tenderResponses, *tender.ToResponse())
	}

	return &SearchResponse{
			Tenders:  tenderResponses,
			Metadata: pagination.Response(total),
		},
		nil
}

// Recommend retrieves tenders with pagination and filtering
func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*SearchResponse, error) {
	s.logger.Info("Recommend tenders", map[string]interface{}{
		"company_id": form.CompanyID,
	})

	company, err := s.companyService.GetByID(ctx, *form.CompanyID)
	if err != nil {
		s.logger.Error("Failed to get company", map[string]interface{}{
			"company_id": form.CompanyID,
			"error":      err.Error(),
		})
		return nil, fmt.Errorf("failed to get company: %w", err)
	}

	form.Status = []string{string(TenderStatusActive)}
	form.Classifications = company.Tags.CPVCodes

	tenders, total, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to list tenders", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, fmt.Errorf("failed to list tenders: %w", err)
	}

	tenderResponses := make([]TenderResponse, 0, len(tenders))
	for _, tender := range tenders {
		tenderResponses = append(tenderResponses, *tender.ToResponse())
	}

	return &SearchResponse{
		Tenders:  tenderResponses,
		Metadata: pagination.Response(total),
	}, nil
}

// Delete deletes a tender

// GetTenderStatistics retrieves tender statistics
func (s *tenderService) GetTenderStatistics(ctx context.Context) (*TenderStatistics, error) {
	s.logger.Info("Retrieving tender statistics", map[string]interface{}{})

	stats, err := s.repository.GetStatistics(ctx)
	if err != nil {
		s.logger.Error("Failed to retrieve tender statistics", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, fmt.Errorf("failed to retrieve statistics: %w", err)
	}

	return stats, nil
}

// UpdateExpiredTenders updates status of expired tenders
func (s *tenderService) UpdateExpiredTenders(ctx context.Context) error {
	s.logger.Info("Updating expired tenders", map[string]interface{}{})

	// Get tenders that should be expired
	expiredTenders, err := s.repository.GetExpiredTenders(ctx, time.Now().Unix())
	if err != nil {
		s.logger.Error("Failed to get expired tenders", map[string]interface{}{
			"error": err.Error(),
		})
		return fmt.Errorf("failed to get expired tenders: %w", err)
	}

	updatedCount := 0
	for _, tender := range expiredTenders {
		if tender.Status != TenderStatusExpired {
			tender.UpdateStatus(TenderStatusExpired)
			if err := s.repository.Update(ctx, &tender); err != nil {
				s.logger.Error("Failed to update expired tender", map[string]interface{}{
					"tender_id": tender.ID,
					"error":     err.Error(),
				})
				continue
			}
			updatedCount++
		}
	}

	s.logger.Info("Expired tenders updated", map[string]interface{}{
		"total_expired": len(expiredTenders),
		"updated":       updatedCount,
	})

	return nil
}

// CleanupOldData removes old data based on retention policy
func (s *tenderService) CleanupOldData(ctx context.Context, olderThan time.Duration, pagination *response.Pagination) error {
	s.logger.Info("Cleaning up old data", map[string]interface{}{
		"older_than": olderThan.String(),
		"limit":      pagination.Limit,
		"offset":     pagination.Offset,
	})

	cutoffTime := time.Now().Add(-olderThan).Unix()

	// For now, we'll just log what would be cleaned up
	// In production, you might want to actually delete old data
	form := &SearchForm{
		Status: []string{string(TenderStatusExpired), string(TenderStatusCancelled), string(TenderStatusAwarded)},
	}

	oldTenders, _, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to find old tenders for cleanup", map[string]interface{}{
			"error": err.Error(),
		})
		return fmt.Errorf("failed to find old tenders: %w", err)
	}

	cleanupCount := 0
	for _, tender := range oldTenders {
		if tender.CreatedAt < cutoffTime {
			cleanupCount++
			// Here you would delete the tender if implementing actual cleanup
			// s.repository.Delete(ctx, tender.ID)
		}
	}

	s.logger.Info("Old data cleanup completed", map[string]interface{}{
		"found_old_tenders": cleanupCount,
		"cutoff_time":       cutoffTime,
	})

	return nil
}
