package storage

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

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

// S3Storage handles S3-compatible object storage operations
type S3Storage struct {
	client     *s3.Client
	presigner  *s3.PresignClient
	bucket     string
	endpoint   string
}

// NewS3Storage creates a new S3 storage service
func NewS3Storage() (*S3Storage, error) {
	region := os.Getenv("AWS_REGION")
	if region == "" {
		region = "us-east-1"
	}

	accessKey := os.Getenv("AWS_ACCESS_KEY_ID")
	secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
	endpoint := os.Getenv("AWS_ENDPOINT")
	bucket := os.Getenv("S3_BUCKET")

	if accessKey == "" || secretKey == "" || bucket == "" {
		return nil, fmt.Errorf("missing required S3 configuration (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET)")
	}

	// Create custom credentials provider
	creds := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")

	// Build S3 config
	cfg, err := config.LoadDefaultConfig(context.Background(),
		config.WithRegion(region),
		config.WithCredentialsProvider(creds),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Create S3 client with custom endpoint for S3-compatible storage (like Civo)
	var client *s3.Client
	if endpoint != "" {
		client = s3.NewFromConfig(cfg, func(o *s3.Options) {
			o.BaseEndpoint = aws.String(endpoint)
			o.UsePathStyle = true // Required for most S3-compatible services
		})
	} else {
		client = s3.NewFromConfig(cfg)
	}

	presigner := s3.NewPresignClient(client)

	return &S3Storage{
		client:    client,
		presigner: presigner,
		bucket:    bucket,
		endpoint:  endpoint,
	}, nil
}

// GenerateUploadURL generates a pre-signed URL for uploading a file
func (s *S3Storage) GenerateUploadURL(key string, contentType string, expiryMinutes int) (string, error) {
	if expiryMinutes <= 0 {
		expiryMinutes = 15 // Default 15 minutes
	}

	input := &s3.PutObjectInput{
		Bucket:      aws.String(s.bucket),
		Key:         aws.String(key),
		ContentType: aws.String(contentType),
	}

	presignResult, err := s.presigner.PresignPutObject(context.Background(), input,
		s3.WithPresignExpires(time.Duration(expiryMinutes)*time.Minute))
	if err != nil {
		return "", fmt.Errorf("failed to generate upload URL: %w", err)
	}

	return presignResult.URL, nil
}

// GenerateDownloadURL generates a pre-signed URL for downloading a file
func (s *S3Storage) GenerateDownloadURL(key string, expiryMinutes int) (string, error) {
	if expiryMinutes <= 0 {
		expiryMinutes = 60 // Default 1 hour
	}

	input := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}

	presignResult, err := s.presigner.PresignGetObject(context.Background(), input,
		s3.WithPresignExpires(time.Duration(expiryMinutes)*time.Minute))
	if err != nil {
		return "", fmt.Errorf("failed to generate download URL: %w", err)
	}

	return presignResult.URL, nil
}

// DeleteObject deletes an object from the bucket
func (s *S3Storage) DeleteObject(key string) error {
	input := &s3.DeleteObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}

	_, err := s.client.DeleteObject(context.Background(), input)
	if err != nil {
		return fmt.Errorf("failed to delete object: %w", err)
	}

	return nil
}

// GetPublicURL returns the public URL for an object (if bucket is public)
func (s *S3Storage) GetPublicURL(key string) string {
	if s.endpoint != "" {
		return fmt.Sprintf("%s/%s/%s", s.endpoint, s.bucket, key)
	}
	return fmt.Sprintf("https://%s.s3.amazonaws.com/%s", s.bucket, key)
}

// GetBucket returns the bucket name
func (s *S3Storage) GetBucket() string {
	return s.bucket
}
