package service

import (
	"context"
	"fmt"
	"io"
	"mime/multipart"
	"path"
	"strings"
	"sync"

	"github.com/pocket-id/pocket-id/backend/internal/apperror"
	"github.com/pocket-id/pocket-id/backend/internal/storage"
	"github.com/pocket-id/pocket-id/backend/internal/utils"
	imageutil "github.com/pocket-id/pocket-id/backend/internal/utils/image"
)

type AppImagesService struct {
	mu         sync.RWMutex
	extensions map[string]string
	storage    storage.FileStorage
}

func NewAppImagesService(extensions map[string]string, storage storage.FileStorage) *AppImagesService {
	return &AppImagesService{extensions: extensions, storage: storage}
}

func (s *AppImagesService) GetImage(ctx context.Context, name string) (io.ReadCloser, int64, string, error) {
	ext, err := s.getExtension(name)
	if err != nil {
		return nil, 0, "", err
	}

	mimeType := utils.GetImageMimeType(ext)
	if mimeType == "" {
		return nil, 0, "", fmt.Errorf("unsupported image type '%s'", ext)
	}

	imagePath := path.Join("application-images", name+"."+ext)
	reader, size, err := s.storage.Open(ctx, imagePath)
	if err != nil {
		if storage.IsNotExist(err) {
			return nil, 0, "", apperror.ImageNotFound()
		}
		return nil, 0, "", err
	}
	return reader, size, mimeType, nil
}

func (s *AppImagesService) UpdateImage(ctx context.Context, file *multipart.FileHeader, imageName string) error {
	fileType := strings.ToLower(utils.GetFileExtension(file.Filename))
	mimeType := utils.GetImageMimeType(fileType)
	if mimeType == "" {
		return apperror.UnsupportedFileType("")
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	currentExt := s.extensions[imageName]

	imagePath := path.Join("application-images", imageName+"."+fileType)
	fileReader, err := file.Open()
	if err != nil {
		return err
	}
	defer fileReader.Close()

	strippedReader, err := imageutil.StripMetadata(fileReader, fileType)
	if err != nil {
		return err
	}

	if err := s.storage.Save(ctx, imagePath, strippedReader); err != nil {
		return err
	}

	if currentExt != "" && currentExt != fileType {
		oldImagePath := path.Join("application-images", imageName+"."+currentExt)
		if err := s.storage.Delete(ctx, oldImagePath); err != nil {
			return err
		}
	}
	s.extensions[imageName] = fileType

	if err := s.storage.Delete(ctx, deletedApplicationImagePath(imageName)); err != nil {
		return err
	}

	return nil
}

func (s *AppImagesService) DeleteImage(ctx context.Context, imageName string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	ext, ok := s.extensions[imageName]
	if !ok || ext == "" {
		return apperror.ImageNotFound()
	}

	if err := s.storage.Save(ctx, deletedApplicationImagePath(imageName), strings.NewReader("")); err != nil {
		return err
	}

	imagePath := path.Join("application-images", imageName+"."+ext)
	if err := s.storage.Delete(ctx, imagePath); err != nil {
		return err
	}

	delete(s.extensions, imageName)
	return nil
}

func deletedApplicationImagePath(imageName string) string {
	return path.Join("application-images", ".deleted", imageName)
}

func (s *AppImagesService) IsDefaultProfilePictureSet() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()

	_, ok := s.extensions["default-profile-picture"]
	return ok
}

func (s *AppImagesService) getExtension(name string) (string, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	ext, ok := s.extensions[name]
	if !ok || ext == "" {
		return "", apperror.ImageNotFound()
	}

	return strings.ToLower(ext), nil
}
