package processor

import (
	"context"
	"fmt"

	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/mappers"
	"apskel-pos-be/internal/models"

	"github.com/google/uuid"
)

type VendorProcessor interface {
	CreateVendor(ctx context.Context, organizationID uuid.UUID, req *models.CreateVendorRequest) (*models.VendorResponse, error)
	UpdateVendor(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdateVendorRequest) (*models.VendorResponse, error)
	DeleteVendor(ctx context.Context, id, organizationID uuid.UUID) error
	GetVendorByID(ctx context.Context, id, organizationID uuid.UUID) (*models.VendorResponse, error)
	ListVendors(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.VendorResponse, int, error)
	GetActiveVendors(ctx context.Context, organizationID uuid.UUID) ([]*models.VendorResponse, error)
}

type VendorProcessorImpl struct {
	vendorRepo VendorRepository
}

func NewVendorProcessorImpl(vendorRepo VendorRepository) *VendorProcessorImpl {
	return &VendorProcessorImpl{
		vendorRepo: vendorRepo,
	}
}

func (p *VendorProcessorImpl) CreateVendor(ctx context.Context, organizationID uuid.UUID, req *models.CreateVendorRequest) (*models.VendorResponse, error) {
	// Check if vendor with same name already exists in organization
	if req.Name != "" {
		existingVendor, err := p.vendorRepo.GetByName(ctx, req.Name, organizationID)
		if err == nil && existingVendor != nil {
			return nil, fmt.Errorf("vendor with name %s already exists in this organization", req.Name)
		}
	}

	// Check if vendor with same email already exists in organization
	if req.Email != nil && *req.Email != "" {
		existingVendor, err := p.vendorRepo.GetByEmail(ctx, *req.Email, organizationID)
		if err == nil && existingVendor != nil {
			return nil, fmt.Errorf("vendor with email %s already exists in this organization", *req.Email)
		}
	}

	vendorEntity := &entities.Vendor{
		OrganizationID: organizationID,
		Name:           req.Name,
		Email:          req.Email,
		PhoneNumber:    req.PhoneNumber,
		Address:        req.Address,
		ContactPerson:  req.ContactPerson,
		TaxNumber:      req.TaxNumber,
		PaymentTerms:   req.PaymentTerms,
		Notes:          req.Notes,
		IsActive:       true, // Default to active
	}

	if req.IsActive != nil {
		vendorEntity.IsActive = *req.IsActive
	}

	err := p.vendorRepo.Create(ctx, vendorEntity)
	if err != nil {
		return nil, fmt.Errorf("failed to create vendor: %w", err)
	}

	return mappers.VendorEntityToResponse(vendorEntity), nil
}

func (p *VendorProcessorImpl) UpdateVendor(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdateVendorRequest) (*models.VendorResponse, error) {
	vendorEntity, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("vendor not found: %w", err)
	}

	// Check if vendor with same name already exists (excluding current vendor)
	if req.Name != nil && *req.Name != "" && *req.Name != vendorEntity.Name {
		existingVendor, err := p.vendorRepo.GetByName(ctx, *req.Name, organizationID)
		if err == nil && existingVendor != nil && existingVendor.ID != id {
			return nil, fmt.Errorf("vendor with name %s already exists in this organization", *req.Name)
		}
	}

	// Check if vendor with same email already exists (excluding current vendor)
	if req.Email != nil && *req.Email != "" && (vendorEntity.Email == nil || *req.Email != *vendorEntity.Email) {
		existingVendor, err := p.vendorRepo.GetByEmail(ctx, *req.Email, organizationID)
		if err == nil && existingVendor != nil && existingVendor.ID != id {
			return nil, fmt.Errorf("vendor with email %s already exists in this organization", *req.Email)
		}
	}

	// Update fields
	if req.Name != nil {
		vendorEntity.Name = *req.Name
	}
	if req.Email != nil {
		vendorEntity.Email = req.Email
	}
	if req.PhoneNumber != nil {
		vendorEntity.PhoneNumber = req.PhoneNumber
	}
	if req.Address != nil {
		vendorEntity.Address = req.Address
	}
	if req.ContactPerson != nil {
		vendorEntity.ContactPerson = req.ContactPerson
	}
	if req.TaxNumber != nil {
		vendorEntity.TaxNumber = req.TaxNumber
	}
	if req.PaymentTerms != nil {
		vendorEntity.PaymentTerms = req.PaymentTerms
	}
	if req.Notes != nil {
		vendorEntity.Notes = req.Notes
	}
	if req.IsActive != nil {
		vendorEntity.IsActive = *req.IsActive
	}

	err = p.vendorRepo.Update(ctx, vendorEntity)
	if err != nil {
		return nil, fmt.Errorf("failed to update vendor: %w", err)
	}

	return mappers.VendorEntityToResponse(vendorEntity), nil
}

func (p *VendorProcessorImpl) DeleteVendor(ctx context.Context, id, organizationID uuid.UUID) error {
	_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
	if err != nil {
		return fmt.Errorf("vendor not found: %w", err)
	}

	err = p.vendorRepo.Delete(ctx, id)
	if err != nil {
		return fmt.Errorf("failed to delete vendor: %w", err)
	}

	return nil
}

func (p *VendorProcessorImpl) GetVendorByID(ctx context.Context, id, organizationID uuid.UUID) (*models.VendorResponse, error) {
	vendorEntity, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("vendor not found: %w", err)
	}

	return mappers.VendorEntityToResponse(vendorEntity), nil
}

func (p *VendorProcessorImpl) ListVendors(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.VendorResponse, int, error) {
	offset := (page - 1) * limit
	vendorEntities, total, err := p.vendorRepo.List(ctx, organizationID, filters, limit, offset)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to list vendors: %w", err)
	}

	vendorResponses := mappers.VendorEntitiesToResponses(vendorEntities)
	totalPages := int((total + int64(limit) - 1) / int64(limit))

	return vendorResponses, totalPages, nil
}

func (p *VendorProcessorImpl) GetActiveVendors(ctx context.Context, organizationID uuid.UUID) ([]*models.VendorResponse, error) {
	vendorEntities, err := p.vendorRepo.GetActiveVendors(ctx, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get active vendors: %w", err)
	}

	return mappers.VendorEntitiesToResponses(vendorEntities), nil
}
