package services

import (
	"fmt"
	"mime/multipart"
	"os"
	"path/filepath"

	"rijig/dto"
	"rijig/internal/repositories"
	"rijig/model"
	"rijig/utils"

	"github.com/google/uuid"
)

type StoreService interface {
	CreateStore(userID string, storeDTO dto.RequestStoreDTO, storeLogo *multipart.FileHeader, storeBanner *multipart.FileHeader) (*dto.ResponseStoreDTO, error)
	GetStoreByUserID(userID string) (*dto.ResponseStoreDTO, error)
	UpdateStore(storeID string, storeDTO *dto.RequestStoreDTO, storeLogo *multipart.FileHeader, storeBanner *multipart.FileHeader, userID string) (*dto.ResponseStoreDTO, error)
	DeleteStore(storeID string) error
}

type storeService struct {
	storeRepo repositories.StoreRepository
}

func NewStoreService(storeRepo repositories.StoreRepository) StoreService {
	return &storeService{storeRepo}
}

func (s *storeService) CreateStore(userID string, storeDTO dto.RequestStoreDTO, storeLogo, storeBanner *multipart.FileHeader) (*dto.ResponseStoreDTO, error) {

	if errors, valid := storeDTO.ValidateStoreInput(); !valid {
		return nil, fmt.Errorf("validation error: %v", errors)
	}

	existingStore, err := s.storeRepo.FindStoreByUserID(userID)
	if err != nil {
		return nil, fmt.Errorf("error checking if user already has a store: %w", err)
	}
	if existingStore != nil {
		return nil, fmt.Errorf("user already has a store")
	}

	address, err := s.storeRepo.FindAddressByID(storeDTO.StoreAddressID)
	if err != nil {
		return nil, fmt.Errorf("error validating store address ID: %w", err)
	}
	if address == nil {
		return nil, fmt.Errorf("store address ID not found")
	}

	storeLogoPath, err := s.saveStoreImage(storeLogo, "logo")
	if err != nil {
		return nil, fmt.Errorf("failed to save store logo: %w", err)
	}

	storeBannerPath, err := s.saveStoreImage(storeBanner, "banner")
	if err != nil {
		return nil, fmt.Errorf("failed to save store banner: %w", err)
	}

	store := model.Store{
		UserID:         userID,
		StoreName:      storeDTO.StoreName,
		StoreLogo:      storeLogoPath,
		StoreBanner:    storeBannerPath,
		StoreInfo:      storeDTO.StoreInfo,
		StoreAddressID: storeDTO.StoreAddressID,
	}

	if err := s.storeRepo.CreateStore(&store); err != nil {
		return nil, fmt.Errorf("failed to create store: %w", err)
	}

	createdAt, _ := utils.FormatDateToIndonesianFormat(store.CreatedAt)
	updatedAt, _ := utils.FormatDateToIndonesianFormat(store.UpdatedAt)

	storeResponseDTO := &dto.ResponseStoreDTO{
		ID:             store.ID,
		UserID:         store.UserID,
		StoreName:      store.StoreName,
		StoreLogo:      store.StoreLogo,
		StoreBanner:    store.StoreBanner,
		StoreInfo:      store.StoreInfo,
		StoreAddressID: store.StoreAddressID,
		TotalProduct:   store.TotalProduct,
		Followers:      store.Followers,
		CreatedAt:      createdAt,
		UpdatedAt:      updatedAt,
	}

	return storeResponseDTO, nil
}

func (s *storeService) GetStoreByUserID(userID string) (*dto.ResponseStoreDTO, error) {

	store, err := s.storeRepo.FindStoreByUserID(userID)
	if err != nil {
		return nil, fmt.Errorf("error retrieving store by user ID: %w", err)
	}
	if store == nil {
		return nil, fmt.Errorf("store not found for user ID: %s", userID)
	}

	createdAt, err := utils.FormatDateToIndonesianFormat(store.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("failed to format createdAt: %w", err)
	}

	updatedAt, err := utils.FormatDateToIndonesianFormat(store.UpdatedAt)
	if err != nil {
		return nil, fmt.Errorf("failed to format updatedAt: %w", err)
	}

	storeResponseDTO := &dto.ResponseStoreDTO{
		ID:             store.ID,
		UserID:         store.UserID,
		StoreName:      store.StoreName,
		StoreLogo:      store.StoreLogo,
		StoreBanner:    store.StoreBanner,
		StoreInfo:      store.StoreInfo,
		StoreAddressID: store.StoreAddressID,
		TotalProduct:   store.TotalProduct,
		Followers:      store.Followers,
		CreatedAt:      createdAt,
		UpdatedAt:      updatedAt,
	}

	return storeResponseDTO, nil
}

func (s *storeService) UpdateStore(storeID string, storeDTO *dto.RequestStoreDTO, storeLogo, storeBanner *multipart.FileHeader, userID string) (*dto.ResponseStoreDTO, error) {
	store, err := s.storeRepo.FindStoreByID(storeID)
	if err != nil {
		return nil, fmt.Errorf("error retrieving store by ID: %w", err)
	}
	if store == nil {
		return nil, fmt.Errorf("store not found")
	}

	if storeDTO.StoreAddressID == "" {
		return nil, fmt.Errorf("store address ID cannot be empty")
	}

	address, err := s.storeRepo.FindAddressByID(storeDTO.StoreAddressID)
	if err != nil {
		return nil, fmt.Errorf("error validating store address ID: %w", err)
	}
	if address == nil {
		return nil, fmt.Errorf("store address ID not found")
	}

	if storeLogo != nil {
		if err := s.deleteStoreImage(store.StoreLogo); err != nil {
			return nil, fmt.Errorf("failed to delete old store logo: %w", err)
		}
		storeLogoPath, err := s.saveStoreImage(storeLogo, "logo")
		if err != nil {
			return nil, fmt.Errorf("failed to save store logo: %w", err)
		}
		store.StoreLogo = storeLogoPath
	}

	if storeBanner != nil {
		if err := s.deleteStoreImage(store.StoreBanner); err != nil {
			return nil, fmt.Errorf("failed to delete old store banner: %w", err)
		}
		storeBannerPath, err := s.saveStoreImage(storeBanner, "banner")
		if err != nil {
			return nil, fmt.Errorf("failed to save store banner: %w", err)
		}
		store.StoreBanner = storeBannerPath
	}

	store.StoreName = storeDTO.StoreName
	store.StoreInfo = storeDTO.StoreInfo
	store.StoreAddressID = storeDTO.StoreAddressID

	if err := s.storeRepo.UpdateStore(store); err != nil {
		return nil, fmt.Errorf("failed to update store: %w", err)
	}

	createdAt, err := utils.FormatDateToIndonesianFormat(store.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("failed to format createdAt: %w", err)
	}
	updatedAt, err := utils.FormatDateToIndonesianFormat(store.UpdatedAt)
	if err != nil {
		return nil, fmt.Errorf("failed to format updatedAt: %w", err)
	}

	storeResponseDTO := &dto.ResponseStoreDTO{
		ID:             store.ID,
		UserID:         store.UserID,
		StoreName:      store.StoreName,
		StoreLogo:      store.StoreLogo,
		StoreBanner:    store.StoreBanner,
		StoreInfo:      store.StoreInfo,
		StoreAddressID: store.StoreAddressID,
		TotalProduct:   store.TotalProduct,
		Followers:      store.Followers,
		CreatedAt:      createdAt,
		UpdatedAt:      updatedAt,
	}

	return storeResponseDTO, nil
}

func (s *storeService) DeleteStore(storeID string) error {
	store, err := s.storeRepo.FindStoreByID(storeID)
	if err != nil {
		return fmt.Errorf("error retrieving store by ID: %w", err)
	}
	if store == nil {
		return fmt.Errorf("store not found")
	}

	if store.StoreLogo != "" {
		if err := s.deleteStoreImage(store.StoreLogo); err != nil {
			return fmt.Errorf("failed to delete store logo: %w", err)
		}
	}

	if store.StoreBanner != "" {
		if err := s.deleteStoreImage(store.StoreBanner); err != nil {
			return fmt.Errorf("failed to delete store banner: %w", err)
		}
	}

	if err := s.storeRepo.DeleteStore(storeID); err != nil {
		return fmt.Errorf("failed to delete store: %w", err)
	}

	return nil
}

func (s *storeService) saveStoreImage(file *multipart.FileHeader, imageType string) (string, error) {

	imageDir := fmt.Sprintf("./public%s/uploads/store/%s", os.Getenv("BASE_URL"), imageType)
	if _, err := os.Stat(imageDir); os.IsNotExist(err) {

		if err := os.MkdirAll(imageDir, os.ModePerm); err != nil {
			return "", fmt.Errorf("failed to create directory for %s image: %v", imageType, err)
		}
	}

	allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
	extension := filepath.Ext(file.Filename)
	if !allowedExtensions[extension] {
		return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed for %s", imageType)
	}

	fileName := fmt.Sprintf("%s_%s%s", imageType, uuid.New().String(), extension)
	filePath := filepath.Join(imageDir, fileName)

	fileData, err := file.Open()
	if err != nil {
		return "", fmt.Errorf("failed to open file: %w", err)
	}
	defer fileData.Close()

	outFile, err := os.Create(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to create %s image file: %v", imageType, err)
	}
	defer outFile.Close()

	if _, err := outFile.ReadFrom(fileData); err != nil {
		return "", fmt.Errorf("failed to save %s image: %v", imageType, err)
	}

	return filepath.Join("/uploads/store", imageType, fileName), nil
}

func (s *storeService) deleteStoreImage(imagePath string) error {
	if imagePath == "" {
		return nil
	}

	filePath := filepath.Join("./public", imagePath)

	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return nil
	}

	err := os.Remove(filePath)
	if err != nil {
		return fmt.Errorf("failed to delete file at %s: %w", filePath, err)
	}

	return nil
}
