package filesystem

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"unprism.ru/KRBL/agate"
	"unprism.ru/KRBL/agate/store"
)

const blobExtension = ".zip"

// fileSystemStore реализует интерфейс store.BlobStore с использованием локальной файловой системы.
type fileSystemStore struct {
	baseDir string // Директория для хранения блобов (архивов)
}

// NewFileSystemStore создает новое хранилище блобов в указанной директории.
func NewFileSystemStore(baseDir string) (store.BlobStore, error) {
	// Убедимся, что директория существует
	if err := os.MkdirAll(baseDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create base directory %s for filesystem blob store: %w", baseDir, err)
	}
	return &fileSystemStore{baseDir: baseDir}, nil
}

// getBlobPath формирует полный путь к файлу блоба.
func (fs *fileSystemStore) getBlobPath(snapshotID string) string {
	// Используем ID снапшота в качестве имени файла
	return filepath.Join(fs.baseDir, snapshotID+blobExtension)
}

// StoreBlob сохраняет данные из reader в файл в baseDir.
func (fs *fileSystemStore) StoreBlob(ctx context.Context, snapshotID string, reader io.Reader) (string, error) {
	blobPath := fs.getBlobPath(snapshotID)

	// Создаем или перезаписываем файл
	file, err := os.Create(blobPath)
	if err != nil {
		return "", fmt.Errorf("failed to create blob file %s: %w", blobPath, err)
	}
	defer file.Close() // Гарантируем закрытие файла

	// Копируем данные из ридера в файл
	_, err = io.Copy(file, reader)
	if err != nil {
		// Если произошла ошибка копирования, удаляем неполный файл
		os.Remove(blobPath)
		return "", fmt.Errorf("failed to write data to blob file %s: %w", blobPath, err)
	}

	// Возвращаем путь к созданному файлу
	return blobPath, nil
}

// RetrieveBlob открывает файл блоба и возвращает его как io.ReadCloser.
func (fs *fileSystemStore) RetrieveBlob(ctx context.Context, snapshotID string) (io.ReadCloser, error) {
	blobPath := fs.getBlobPath(snapshotID)

	// Открываем файл для чтения
	file, err := os.Open(blobPath)
	if err != nil {
		if os.IsNotExist(err) {
			// Если файл не найден, возвращаем кастомную ошибку
			return nil, agate.ErrNotFound
		}
		return nil, fmt.Errorf("failed to open blob file %s: %w", blobPath, err)
	}

	// Возвращаем открытый файл (*os.File реализует io.ReadCloser)
	return file, nil
}

// DeleteBlob удаляет файл блоба из файловой системы.
func (fs *fileSystemStore) DeleteBlob(ctx context.Context, snapshotID string) error {
	blobPath := fs.getBlobPath(snapshotID)

	// Удаляем файл
	err := os.Remove(blobPath)
	if err != nil {
		if os.IsNotExist(err) {
			// Если файл и так не существует, это не ошибка
			return nil
		}
		// Если произошла другая ошибка при удалении
		return fmt.Errorf("failed to delete blob file %s: %w", blobPath, err)
	}

	return nil
}

// GetBlobPath возвращает путь к файлу блоба, если он существует.
func (fs *fileSystemStore) GetBlobPath(ctx context.Context, snapshotID string) (string, error) {
	blobPath := fs.getBlobPath(snapshotID)

	// Проверяем существование файла
	if _, err := os.Stat(blobPath); err != nil {
		if os.IsNotExist(err) {
			return "", agate.ErrNotFound
		}
		return "", fmt.Errorf("failed to stat blob file %s: %w", blobPath, err)
	}

	// Файл существует, возвращаем путь
	return blobPath, nil
}
