//go:build dev

package s3

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/aws/aws-sdk-go-v2/aws"
	awsconfig "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"
)

var Client Uploader

type Uploader interface {
	Upload(ctx context.Context, bucket, key string, body io.Reader, contentType string) error
	Download(ctx context.Context, bucket, key string, w io.Writer) error
	Delete(ctx context.Context, bucket, key string) error
	GetURL(ctx context.Context, bucket, key string) (string, error)
	HealthCheck(ctx context.Context) error
}

type S3Client struct {
	client    *s3.Client
	bucket    string
	publicURL string
}

func Connect() error {
	// Check for RUSTFS_* vars first (matching compose.yml), fall back to S3_* vars
	endpoint := os.Getenv("RUSTFS_ENDPOINT")
	if endpoint == "" {
		endpoint = os.Getenv("S3_ENDPOINT")
	}
	if endpoint == "" {
		// Default: localhost for bare metal dev, use rustfs:9000 for docker
		endpoint = "http://localhost:9000"
	}

	accessKey := os.Getenv("RUSTFS_ACCESS_KEY")
	if accessKey == "" {
		accessKey = os.Getenv("S3_ACCESS_KEY")
	}
	if accessKey == "" {
		accessKey = "minioadmin"
	}

	secretKey := os.Getenv("RUSTFS_SECRET_KEY")
	if secretKey == "" {
		secretKey = os.Getenv("S3_SECRET_KEY")
	}
	if secretKey == "" {
		secretKey = "minioadmin"
	}

	bucket := os.Getenv("RUSTFS_BUCKET")
	if bucket == "" {
		bucket = os.Getenv("S3_BUCKET")
	}
	if bucket == "" {
		bucket = "crussell"
	}

	profilePicsBucket := os.Getenv("S3_PROFILE_PICS_BUCKET")
	if profilePicsBucket == "" {
		profilePicsBucket = "crussell-profile-pics"
	}

	region := os.Getenv("AWS_REGION")
	if region == "" {
		region = "eu-west-2"
	}

	publicURL := os.Getenv("S3_PUBLIC_URL")
	if publicURL == "" {
		publicURL = endpoint
	}

	awsCfg, err := awsconfig.LoadDefaultConfig(context.Background(),
		awsconfig.WithRegion(region),
		awsconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
			accessKey,
			secretKey,
			"",
		)),
	)
	if err != nil {
		return fmt.Errorf("failed to load AWS config: %w", err)
	}

	Client = &S3Client{
		client: s3.NewFromConfig(awsCfg, func(o *s3.Options) {
			o.BaseEndpoint = aws.String(endpoint)
			o.UsePathStyle = true
		}),
		bucket:    bucket,
		publicURL: publicURL,
	}

	// Create bucket if it doesn't exist
	ctx := context.Background()
	s3Client := s3.NewFromConfig(awsCfg, func(o *s3.Options) {
		o.BaseEndpoint = aws.String(endpoint)
		o.UsePathStyle = true
	})
	_, err = s3Client.CreateBucket(ctx, &s3.CreateBucketInput{
		Bucket: aws.String(bucket),
	})
	if err != nil {
		log.Printf("Bucket creation: %v (may already exist)", err)
	}

	// Set bucket policy for public read access
	policy := fmt.Sprintf(`{
		"Version": "2012-10-17",
		"Statement": [{
			"Sid": "PublicReadGetObject",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::%s/*"
		}]
	}`, bucket)
	_, err = s3Client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
		Bucket: aws.String(bucket),
		Policy: aws.String(policy),
	})
	if err != nil {
		log.Printf("Bucket policy: %v (may already exist)", err)
	}

	// Create profile pics bucket if it doesn't exist
	if profilePicsBucket != bucket {
		_, err = s3Client.CreateBucket(ctx, &s3.CreateBucketInput{
			Bucket: aws.String(profilePicsBucket),
		})
		if err != nil {
			log.Printf("Profile pics bucket creation: %v (may already exist)", err)
		}

		// Set bucket policy for public read access
		profilePolicy := fmt.Sprintf(`{
			"Version": "2012-10-17",
			"Statement": [{
				"Sid": "PublicReadGetObject",
				"Effect": "Allow",
				"Principal": "*",
				"Action": "s3:GetObject",
				"Resource": "arn:aws:s3:::%s/*"
			}]
		}`, profilePicsBucket)
		_, err = s3Client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
			Bucket: aws.String(profilePicsBucket),
			Policy: aws.String(profilePolicy),
		})
		if err != nil {
			log.Printf("Profile pics bucket policy: %v (may already exist)", err)
		}
	}

	log.Printf("Connected to local S3 (Rustfs): bucket=%s, endpoint=%s", bucket, endpoint)
	return nil
}

func (s *S3Client) Upload(ctx context.Context, bucket, key string, body io.Reader, contentType string) error {
	_, err := s.client.PutObject(ctx, &s3.PutObjectInput{
		Bucket:      aws.String(bucket),
		Key:         aws.String(key),
		Body:        body,
		ContentType: aws.String(contentType),
	})
	return err
}

func (s *S3Client) Download(ctx context.Context, bucket, key string, w io.Writer) error {
	result, err := s.client.GetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	})
	if err != nil {
		return err
	}
	defer result.Body.Close()

	_, err = io.Copy(w, result.Body)
	return err
}

func (s *S3Client) Delete(ctx context.Context, bucket, key string) error {
	_, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	})
	return err
}

func (s *S3Client) GetURL(ctx context.Context, bucket, key string) (string, error) {
	return fmt.Sprintf("%s/%s/%s", s.publicURL, bucket, key), nil
}

func (s *S3Client) HealthCheck(ctx context.Context) error {
	_, err := s.client.HeadBucket(ctx, &s3.HeadBucketInput{
		Bucket: aws.String(s.bucket),
	})
	if err != nil {
		return fmt.Errorf("S3 bucket %q is not accessible (check S3_PUBLIC_URL / RUSTFS_ENDPOINT config): %w", s.bucket, err)
	}
	return nil
}
