// Package storage provides object storage for Where Woof photos (MinIO, S3-compatible).
package storage

import (
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// Client wraps a MinIO client and bucket for photo objects.
type Client struct {
	mc     *minio.Client
	bucket string
}

// NewClient builds a storage client from MINIO_* env vars.
// Returns (nil, nil) when storage is not configured (MINIO_ENDPOINT unset) —
// callers should treat that as "storage unavailable".
func NewClient() (*Client, error) {
	endpoint := os.Getenv("MINIO_ENDPOINT")
	if endpoint == "" {
		return nil, nil
	}
	access := os.Getenv("MINIO_ACCESS_KEY")
	secret := os.Getenv("MINIO_SECRET_KEY")
	bucket := os.Getenv("MINIO_BUCKET")
	if bucket == "" {
		bucket = "wherewoof"
	}

	mc, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(access, secret, ""),
		Secure: false, // internal LAN
	})
	if err != nil {
		return nil, fmt.Errorf("minio client: %w", err)
	}
	return &Client{mc: mc, bucket: bucket}, nil
}

// Put stores an object under key and returns the size.
func (c *Client) Put(ctx context.Context, key string, r io.Reader, size int64, contentType string) error {
	if c == nil {
		return fmt.Errorf("storage not configured")
	}
	_, err := c.mc.PutObject(ctx, c.bucket, key, r, size, minio.PutObjectOptions{
		ContentType: contentType,
	})
	return err
}

// Get returns the object reader + content type for a key.
func (c *Client) Get(ctx context.Context, key string) (io.ReadCloser, string, error) {
	if c == nil {
		return nil, "", fmt.Errorf("storage not configured")
	}
	obj, err := c.mc.GetObject(ctx, c.bucket, key, minio.GetObjectOptions{})
	if err != nil {
		return nil, "", err
	}
	stat, err := obj.Stat()
	if err != nil {
		obj.Close()
		return nil, "", err
	}
	return obj, stat.ContentType, nil
}

// ListKeys returns object keys with the given prefix (used for migration).
func (c *Client) ListKeys(ctx context.Context, prefix string) ([]string, error) {
	if c == nil {
		return nil, fmt.Errorf("storage not configured")
	}
	var keys []string
	for obj := range c.mc.ListObjects(ctx, c.bucket, minio.ListObjectsOptions{Prefix: prefix}) {
		if obj.Err != nil {
			return nil, obj.Err
		}
		keys = append(keys, obj.Key)
	}
	return keys, nil
}

// PresignedURL returns a temporary URL (not used by the app; available for admin previews).
func (c *Client) PresignedURL(ctx context.Context, key string, expiry time.Duration) (string, error) {
	if c == nil {
		return "", fmt.Errorf("storage not configured")
	}
	u, err := c.mc.PresignedGetObject(ctx, c.bucket, key, expiry, nil)
	if err != nil {
		return "", err
	}
	return u.String(), nil
}
