package company

import (
	"context"
	"errors"
	"time"
	"tm/pkg/logger"
	orm "tm/pkg/mongo"
	"tm/pkg/response"

	"go.mongodb.org/mongo-driver/v2/bson"
)

// Repository defines the interface for company data operations
type Repository interface {
	Create(ctx context.Context, company *Company) error
	Update(ctx context.Context, company *Company) error
	GetByID(ctx context.Context, id string) (*Company, error)
	GetByIDs(ctx context.Context, ids []string) ([]Company, error)
	GetByName(ctx context.Context, name string) (*Company, error)
	GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error)
	GetByTaxID(ctx context.Context, taxID string) (*Company, error)
	Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*orm.PaginatedResult[Company], error)
	Delete(ctx context.Context, id string) error
	UpdateStatus(ctx context.Context, id string, status CompanyStatus) error
	UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error
}

// companyRepository implements the Repository interface using the MongoDB ORM
type companyRepository struct {
	ormRepo orm.Repository[Company]
	logger  logger.Logger
}

// NewCompanyRepository creates a new company repository
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
	// Create indexes using the ORM's index management
	indexes := []orm.Index{
		*orm.NewIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
	}

	// Create indexes
	err := mongoManager.CreateIndexes("companies", indexes)
	if err != nil {
		logger.Warn("Failed to create company indexes", map[string]interface{}{
			"error": err.Error(),
		})
	}

	// Create ORM repository
	ormRepo := orm.NewRepository[Company](mongoManager.GetCollection("companies"), logger)

	return &companyRepository{
		ormRepo: ormRepo,
		logger:  logger,
	}
}

func mapCompanySortField(sortBy string) string {
	switch sortBy {
	case "country":
		return "address.country"
	case "state":
		return "address.state"
	default:
		return sortBy
	}
}

// Create creates a new company
func (r *companyRepository) Create(ctx context.Context, company *Company) error {
	// Set created/updated timestamps using Unix timestamps
	now := time.Now().Unix()
	company.SetCreatedAt(now)

	// Use ORM to create company
	err := r.ormRepo.Create(ctx, company)
	if err != nil {
		r.logger.Error("Failed to create company", map[string]interface{}{
			"error":               err.Error(),
			"name":                company.Name,
			"registration_number": company.RegistrationNumber,
		})
		return err
	}

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

	return nil
}

// GetByID retrieves a company by ID
func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, error) {
	company, err := r.ormRepo.FindByID(ctx, id)
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, errors.New("company not found")
		}
		r.logger.Error("Failed to get company by ID", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return nil, err
	}

	return company, nil
}

// GetByIDs retrieves companies by IDs
func (r *companyRepository) GetByIDs(ctx context.Context, ids []string) ([]Company, error) {
	objectIDs := make([]bson.ObjectID, len(ids))
	for i, id := range ids {
		objectID, err := bson.ObjectIDFromHex(id)
		if err != nil {
			return nil, err
		}
		objectIDs[i] = objectID
	}

	companies, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": objectIDs}}, orm.NewPaginationBuilder().Build())
	if err != nil {
		return nil, err
	}

	return companies.Items, nil
}

// GetByName retrieves a company by name
func (r *companyRepository) GetByName(ctx context.Context, name string) (*Company, error) {
	company, err := r.ormRepo.FindOne(ctx, bson.M{"name": name})
	if err != nil {
		return nil, err
	}
	return company, nil
}

// GetByRegistrationNumber retrieves a company by registration number
func (r *companyRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error) {
	company, err := r.ormRepo.FindOne(ctx, bson.M{"registration_number": registrationNumber})
	if err != nil {
		return nil, err
	}
	return company, nil
}

// GetByTaxID retrieves a company by tax ID
func (r *companyRepository) GetByTaxID(ctx context.Context, taxID string) (*Company, error) {
	company, err := r.ormRepo.FindOne(ctx, bson.M{"tax_id": taxID})
	if err != nil {
		return nil, err
	}
	return company, nil
}

// Update updates a company
func (r *companyRepository) Update(ctx context.Context, company *Company) error {
	// Set updated timestamp using Unix timestamp
	company.SetUpdatedAt(time.Now().Unix())

	// Use ORM to update company
	err := r.ormRepo.Update(ctx, company)
	if err != nil {
		r.logger.Error("Failed to update company", map[string]interface{}{
			"error":      err.Error(),
			"company_id": company.ID,
		})
		return err
	}

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

	return nil
}

// Delete deletes a company
func (r *companyRepository) Delete(ctx context.Context, id string) error {
	// Get company first
	_, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Update in database
	err = r.ormRepo.Delete(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete company", map[string]interface{}{
			"error":      err.Error(),
			"company_id": id,
		})
		return err
	}

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

	return nil
}

// Search retrieves companies with search and filters
func (r *companyRepository) Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*orm.PaginatedResult[Company], error) {
	// Build filter
	filter := bson.M{}

	if form.Search != nil {
		filter["$text"] = bson.M{"$search": *form.Search}
	}

	if form.Type != nil {
		filter["type"] = *form.Type
	}

	if form.Status != nil {
		filter["status"] = *form.Status
	}

	if form.Industry != nil {
		filter["industry"] = *form.Industry
	}

	if form.IsVerified != nil {
		filter["is_verified"] = *form.IsVerified
	}

	if form.IsCompliant != nil {
		filter["is_compliant"] = *form.IsCompliant
	}

	if form.Language != nil {
		filter["language"] = *form.Language
	}

	if form.Currency != nil {
		filter["currency"] = *form.Currency
	}

	// Tag filters
	if len(form.CPVCodes) > 0 {
		filter["tags.cpv_codes"] = bson.M{"$in": form.CPVCodes}
	}

	if len(form.Categories) > 0 {
		filter["tags.categories"] = bson.M{"$in": form.Categories}
	}

	if len(form.Keywords) > 0 {
		filter["tags.keywords"] = bson.M{"$in": form.Keywords}
	}

	if len(form.Specializations) > 0 {
		filter["tags.specializations"] = bson.M{"$in": form.Specializations}
	}

	// Range filters
	if form.EmployeeCountMin != nil || form.EmployeeCountMax != nil {
		rangeFilter := bson.M{}
		if form.EmployeeCountMin != nil {
			rangeFilter["$gte"] = *form.EmployeeCountMin
		}
		if form.EmployeeCountMax != nil {
			rangeFilter["$lte"] = *form.EmployeeCountMax
		}
		filter["employee_count"] = rangeFilter
	}

	if form.AnnualRevenueMin != nil || form.AnnualRevenueMax != nil {
		rangeFilter := bson.M{}
		if form.AnnualRevenueMin != nil {
			rangeFilter["$gte"] = *form.AnnualRevenueMin
		}
		if form.AnnualRevenueMax != nil {
			rangeFilter["$lte"] = *form.AnnualRevenueMax
		}
		filter["annual_revenue"] = rangeFilter
	}

	if form.FoundedYearMin != nil || form.FoundedYearMax != nil {
		rangeFilter := bson.M{}
		if form.FoundedYearMin != nil {
			rangeFilter["$gte"] = *form.FoundedYearMin
		}
		if form.FoundedYearMax != nil {
			rangeFilter["$lte"] = *form.FoundedYearMax
		}
		filter["founded_year"] = rangeFilter
	}

	mongoPagination, err := orm.BuildListPagination(
		pagination.Limit,
		pagination.Offset,
		pagination.Cursor,
		mapCompanySortField(pagination.SortBy),
		pagination.SortOrder,
		filter,
		orm.ListPaginationOptions{},
	)
	if err != nil {
		return nil, err
	}

	result, err := r.ormRepo.FindAll(ctx, filter, mongoPagination)
	if err != nil {
		r.logger.Error("Failed to search companies", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, err
	}

	return result, nil
}

// UpdateStatus updates company status
func (r *companyRepository) UpdateStatus(ctx context.Context, id string, status CompanyStatus) error {
	// Get company first
	company, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Update status
	company.Status = status
	company.SetUpdatedAt(time.Now().Unix())

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

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

	return nil
}

// UpdateVerification updates company verification status
func (r *companyRepository) UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error {
	// Get company first
	company, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Update verification fields
	company.IsVerified = isVerified
	company.IsCompliant = isCompliant
	company.ComplianceNotes = complianceNotes
	company.SetUpdatedAt(time.Now().Unix())

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

	r.logger.Info("Company verification updated successfully", map[string]interface{}{
		"company_id":   id,
		"is_verified":  isVerified,
		"is_compliant": isCompliant,
	})

	return nil
}
